Static vector pair of strings class member function - c++

Write a function partlist that gives all the ways to divide a list (an array) of at least two elements into two non-empty parts.
From what I understand, the function should produce linear partitions (using term as mathematical) of the original array.
I think I understand each of the types for the function individually, but I am struggling to bring them all together.
(I have 6 months of C++ experience and no other languages. This is an exercise from codewars that I'm using to try to improve my coding skills)
I've written the function code up to the point where I want to start testing, but with the way the problem is worded, I do not understand how to instantiate the class type. I've reviewed statics, vectors, pairs, and constants in individual terms from class notes and cplusplus.com.
I've gotten to the point that the program will compile, but will not complete main(). I feel like I'm missing a vital bit of information, and I appreciate any help to understand the goal of the program.
#include <iostream>
#include <vector>
class PartList
{
public:
static std::vector<std::pair <std::string, std::string>> partlist(std::vector<std::string> &arr);
};
///The above is what I have to work with///
int main(){
std::vector<std::string> tester = {"I", "Love", "To", "Discrete"};
PartList::partlist(tester);
}
std::vector<std::pair<std::string,std::string>> PartList::partlist(std::vector<std::string> &arr){
std::vector<std::pair<std::string,std::string>> output;
std::vector<std::pair<std::string,std::string>>::iterator bigIt = output.begin();
std::vector<std::string>::iterator myIt;
for(std::vector<std::string>::iterator secIt = arr.begin();
secIt != arr.end(); secIt++){
myIt = arr.begin();
while(myIt <= secIt){
bigIt->first += *myIt;
myIt++;
}
while((myIt > secIt) && myIt != arr.end()){
bigIt->second += *myIt;
myIt++;
}
}
return output;
}
Expected:
For set {std::string a, std::string b, std::string c, std::string d}
Should result in {a, bcd}, {ab,cd}, {abc,d}
Result:
nothing

As john said in comments. you're not actually doing anything with output. At the beginning of your for loop, you need to append a new item to output with output.push_back(). Then, instead of using an iterator, just reference that item using output.back()
code:
using std::vector;
using std::string;
using std::pair;
vector<pair<string, string>> PartList::partlist(vector<string> &arr)
{
vector<pair<string, string>> output;
vector<string>::const_iterator arr_iterator;
for (vector<string>::const_iterator secIt = arr.begin(); secIt != std::prev(arr.end()); secIt++)
{
arr_iterator = arr.begin();
output.push_back(pair<string, string>());
while (arr_iterator <= secIt)
{
output.back().first += *(arr_iterator++);
}
while (arr_iterator != arr.end())
{
output.back().second += *(arr_iterator++);
}
}
return output;
}

Related

Getting a substr from a std::variant<std::string, std::vector<std::string>>> in C++

So I've got a map with a string key and either a string or vector value. I want to cycle through all the string values (whether they're found in a vector or they can be directly checked) in the map and check if they match a given string (it's irrelevant what the string is). If that string matches completely or partly, I will put the map that the string was found in inside a vector.
The issue I'm having is that even though I'm separating strings from vectors using an if conditional the substr function thinks I'm dealing with a std::variant<std::string, std::vector<std::string>>> and therefore gives me an error.
The begin functions in the else functions also tell me that variant doesn't have a member called "begin" because of this reason. I'm totally lost, any help would be great.
for (std::unordered_map<std::string,
std::variant<std::string,
std::vector<std::string>>>::iterator h = MyMap.begin();
h != MyMap.end();
h++) {
if (typeid(h->second) == typeid(std::string)) {
std::string MapValueString = h->second.substr(0, TextCtrlValue.length());
if (MapValueString == TextCtrlValue) {
RightSearchResultsVector.insert(RightSearchResultsVector.end(), Maps.begin(), Maps.end());
}
}
else {
for (std::vector<std::string>::iterator f = h->second.begin(); f != h->second.end(); f++) {
// Go through vector and find matching/semi matching strings
}
}
}
Here's how to tell what type a variant holds
std::string* pstr = std::get_if<std::string>(&h->second);
if (pstr != nullptr)
{
// do stuff with string
}
else
{
std::vector<std::string>& vec = std::get<std::vector<std::string>>(h->second);
// do stuff with vector
}
BTW you can simplify this monstrosity
for (std::unordered_map<std::string,
std::variant<std::string,
std::vector<std::string>>>::iterator h = MyMap.begin();
by writing
for (auto h = MyMap.begin();
Judicious use of auto can greatly improve the readability of your code.

Search for a variable from vector of objects

struct ABC
{
int a;
string b;
};
I have a vector of objects to the above struct. And want to search the vector based on variable "b"?
I have logic as below.
vector<ABC> vec = ...;//vec has my objects
for(vector<ABC>::iterator it = vec.begin();
it != vec.end();
++it)
{
if(search_str == (it->b))//search string is my string which i need to search
{
}
}
I have extensively tested the above code and it works. I want to know if there is a better way to achieve this. Maybe using find().
Simple, readable, lifted from Sam's comment:
auto found = std::find_if(vec.begin(), vec.end(), [&](auto const &e) {
return e.b == search_str;
});
And now found is an iterator to the first matching element, or vec.end() if none was found.
You can also use range based for loops in some cases, give you much clearer code.
for (auto const &p : vec)
{
if (p == search_str)
{
//--- Handle the find ---//
//if you want to stop...
break;
}
}
One of the better method to compare two strings is using compare method in C++.
Suppose you want to compare two strings S1 and S2. You can use equality operator( == ) as you have already used.
But using std::string::compare() function has it's own benefit.
We can not only compare two strings but can also check if one is less or greater.
std::string::compare() function return an int:
zero if S1 is equal to S2.
less than zero if S1 is less than S2.
greater than zero if S1 is greater than S2.
So your code can be formatted as:
vector<ABC> vec = ...;//vec has my objects
for(vector<ABC>::iterator it = vec.begin(); it != vec.end(); ++it){
if(!search_str.compare(it->b))
{
//match found
}
}

Optimized way to partition a class of objects based on an attribute in C++

I have a class that has million of items and each item has a label of type int. I need to partition items based on their similar labels, so at the end I return a vector<MyClass>. First, I sort all items based on their label. Then, in a for loop I compare each label value with previous one and if its the same I store it in a myclass_temp until label != previous_label. If label != previous_label I add this myclass_temp to the vector<MyClass>, and I erase myclass_temp. I think the code is self-explained.
The program works fine, but it is slow, is there a better way to speed it up? I believe because I sort the items in the beginning, there should be a faster way to simply partition items with similar labels.
Second question is how to calculate O score for this algorithm and any suggested faster solution?
please feel free to correct my code.
vector <MyClass> PartitionByLabels(MyClass &myclass){
/// sort MyClass items based on label number
printf ("Sorting items by label number... \n");
std::sort(myclass.begin(), myclass.end(), compare_labels);
vector <MyClass> myClasses_vec;
MyClass myclass_temp;
int previous_label=0, label=0;
int total_items;
/// partition myclass items based on similar labels
for (int i=0; i < myclass.size(); i++){
label = myclass[i].label;
if (label == previous_label){
myclass_temp.push_back(myclass[i]);
previous_label = label;
/// add the last similar items
if (i == myclass.size()-1){
myClasses_vec.push_back(myclass_temp);
total_items +=myclass_temp.size();
}
} else{
myClasses_vec.push_back(myclass_temp);
total_items +=myclass_temp.size();
myclass_temp.EraseItems();
myclass_temp.push_back(myclass[i]);
previous_label = label;
}
}
printf("Total number of items: %d \n", total_items);
return myClasses_vec;
}
This algorithm should do it. I removed the templates to make it easier to check on godbolt.
Should be easy enough to put back in.
The O score for this method is that of std::sort - O(N.log(N))
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
struct thing
{
std::string label;
std::string value;
};
using MyClass = std::vector<thing>;
using Partitions = std::vector<MyClass>;
auto compare_labels = [](thing const& l, thing const& r) {
return l.label < r.label;
};
// pass by value - we need a copy anyway and we might get copy elision
Partitions PartitionByLabels(MyClass myclass){
/// sort MyClass items based on label number
std::sort(myclass.begin(), myclass.end(), compare_labels);
Partitions result;
auto first = myclass.begin();
auto last = myclass.end();
// because the range is sorted, we can partition it in linear time.
// choosing the correct algorithm is always the best optimisation
while (first != last)
{
auto next = std::find_if(first, last, [&first](auto const& x) { return x.label != first->label; });
// let's move the items - that should speed things up a little
// this is safe because we took a copy
result.push_back(MyClass(std::make_move_iterator(first),
std::make_move_iterator(next)));
first = next;
}
return result;
}
We can of course do better with unordered maps, if:
the label is hashable and equality-comparable
we don't need to order the output (if we did, we'd use a multimap instead)
The O-score for this method is linear time O(N)
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
#include <unordered_map>
struct thing
{
std::string label;
std::string value;
};
using MyClass = std::vector<thing>;
using Partitions = std::vector<MyClass>;
// pass by value - we need a copy anyway and we might get copy elision
Partitions PartitionByLabels(MyClass const& myclass){
using object_type = MyClass::value_type;
using label_type = decltype(std::declval<object_type>().label);
using value_type = decltype(std::declval<object_type>().value);
std::unordered_multimap<label_type, value_type> inter;
for(auto&& x : myclass) {
inter.emplace(x.label, x.value);
}
Partitions result;
auto first = inter.begin();
auto last = inter.end();
while (first != last)
{
auto range = inter.equal_range(first->first);
MyClass tmp;
tmp.reserve(std::distance(range.first, range.second));
for (auto i = range.first ; i != range.second ; ++i) {
tmp.push_back(object_type{i->first, std::move(i->second)});
}
result.push_back(std::move(tmp));
first = range.second;
}
return result;
}
Why not create a map from ints to vectors, iterate through the original vector once, adding each MyClass object to TheMap[myclass[i].label]? It takes your average runtime from f(n + n*log(n)) to f(n).

Looping on C++ iterators starting with second (or nth) item

I am looking for a readable, elegant way to do the following in C++, here shown in Python:
for datum in data[1:]:
# do work.
The iterators on the data in question may not support random access iterators, so I can't just use:
for (mIter = data.begin() + 1; mIter != data.end(); mIter++)
The best I've come up with is the following:
iterable::iterator mIter = data.begin();
for (mIter++; mIter != allMjds.end(); mjdIter++) {
// do work.
}
It's not too lengthy, but it's hardly expository - at first glance it actually looks like a mistake!
Another solution is to have an "nth element" helper function, I guess. Is there a more concise way?
You can use std::next(iter, n) for a linear-time advance. You can also use the standard std::advance algorithm, though it isn't as simple to use (it takes the iterator by a non-const reference and doesn't return it).
For example,
for (mIter = std::next(data.begin()); mIter != data.end(); ++mIter)
or,
mIter = data.begin();
std::advance(mIter, 1);
for (; mIter != data.end(); ++mIter)
Note that you must make sure that data.size() >= 1, otherwise the code will fail in a catastrophic manner.
#include <iterator>
iterator iter = data.begin();
for (advance(iter, 1); iter != data.end(); ++iter)
{
// do work
}
This relies on >= 1 element in data to avoid an exception, though.
You could try:
for (mIter = data.begin() ; ++mIter != data.end() ; )
but you'd need to make sure that if data.begin () == data.end () doing the ++mIter doesn't cause a problem.
Since this is a non-standard for loop, using a while loop might be more appropriate as there are fewer preconceived ideas about how they work, i.e. people looking at your code are more likely to read a while statement than a for statement as there is usually a model of how a for loop should work in their head.
mIter = data.begin ();
while (++mIter != data.end ())
{
}
You can use boost::next for this (but you should be sure that the list actually has an element in it before doing so):
#include <algorithm>
#include <iostream>
#include <iterator>
#include <list>
#include <boost/assign.hpp>
#include <boost/next_prior.hpp>
using namespace boost::assign;
int main()
{
std::list<int> lst = list_of(23)(9)(84)(24)(12)(18);
std::copy(boost::next(lst.begin()), lst.end(), std::ostream_iterator<int>(std::cout, " "));
return 0;
}
iterable::iterator mIter = data.begin();
std::for_each(++mIter, data.end(), some_func);
where some_func contains the code you want to execute... you could even trivialise it with a simple wrapper function
template <typename _cont, typename _func>
for_1_to_end(_cont const& container, some_func func)
{
typename _cont::const_iterator it = _cont.begin();
std::for_each(++it, _cont.end(), func);
}
This is how i would do it
// starting position in the list
int i = 4;
// initialize "it" to point to the first item of data.
std::list<int>::iterator it = data_list.begin();
if (i < data.size()) {
// loop starting from 4 to end of the list.
for (std::advance(it, i); it != token_list.end(); it++) {
//use "it" here
}
}
else {
// Error: starting point is greater than size of data
}
What might be a good solution in a modern c++ way :
std::for_each(cbegin(data)+1,cend(data),[&](const auto& elem)
{
//do whatever you want with elem here
});
This will work even if data is empty. It's basically possible to use this in the exact same way as you would do it with a standard for-range loop and has the advantage not to require any additional variable while keeping the code readable.
Algorithm library : https://en.cppreference.com/w/cpp/algorithm

Concise lists/vectors in C++

I'm currently translating an algorithm in Python to C++.
This line EXCH_SYMBOL_SETS = [["i", "1", "l"], ["s", "5"], ["b", "8"], ["m", "n"]]
is now
vector<vector<char>> exch_symbols;
vector<char> vector_1il;
vector_1il.push_back('1');
vector_1il.push_back('i');
vector_1il.push_back('l');
vector<char> vector_5s;
vector_5s.push_back('5');
vector_5s.push_back('s');
vector<char> vector_8b;
vector_8b.push_back('8');
vector_8b.push_back('b');
vector<char> vector_mn;
vector_mn.push_back('m');
vector_mn.push_back('n');
exch_symbols.push_back(vector_1il);
exch_symbols.push_back(vector_5s);
exch_symbols.push_back(vector_8b);
exch_symbols.push_back(vector_mn);
I hate to have an intermediate named variable for each inner variable in a 2-D vector. I'm not really familiar with multidimensional datastructures in C++. Is there a better way?
What's happening afterwards is this:
multimap<char, char> exch_symbol_map;
/*# Insert all possibilities
for symbol_set in EXCH_SYMBOL_SETS:
for symbol in symbol_set:
for symbol2 in symbol_set:
if symbol != symbol2:
exch_symbol_map[symbol].add(symbol2)*/
void insert_all_exch_pairs(const vector<vector<char>>& exch_symbols) {
for (vector<vector<char>>::const_iterator symsets_it = exch_symbols.begin();
symsets_it != exch_symbols.end(); ++symsets_it) {
for (vector<char>::const_iterator sym1_it = symsets_it->begin();
sym1_it != symsets_it->end(); ++sym1_it) {
for (vector<char>::const_iterator sym2_it = symsets_it->begin();
sym2_it != symsets_it->end(); ++sym2_it) {
if (sym1_it != sym2_it) {
exch_symbol_map.insert(pair<char, char>(*sym1_it, *sym2_it));
}
}
}
}
}
So this algorithm should work in one way or another with the representation here. The goal is that EXCH_SYMBOL_SETS can be easily changed later to include new groups of chars or add new letters to existing groups. Thank you!
I would refactor, instead of vector<char>, use std::string as internal, i.e.
vector<string> exch_symbols;
exch_symbols.push_back("1il");
exch_symbols.push_back("s5");
exch_symbols.push_back("b8");
exch_symbols.push_back("mn");
then change your insert method:
void insert_all_exch_pairs(const vector<string>& exch_symbols)
{
for (vector<string>::const_iterator symsets_it = exch_symbols.begin(); symsets_it != exch_symbols.end(); ++symsets_it)
{
for (string::const_iterator sym1_it = symsets_it->begin(); sym1_it != symsets_it->end(); ++sym1_it)
{
for (string::const_iterator sym2_it = symsets_it->begin(); sym2_it != symsets_it->end(); ++sym2_it)
{
if (sym1_it != sym2_it)
exch_symbol_map.insert(pair<char, char>(*sym1_it, *sym2_it));
}
}
}
}
You could shorten it by getting rid of the intermediate values
vector<vector<char> > exch_symbols(4, vector<char>()); //>> is not valid in C++98 btw.
//exch_symbols[0].reserve(3)
exch_symbols[0].push_back('i');
etc.
You could also use boost.assign or something similiar
EXCH_SYMBOL_SETS = [["i", "1", "l"], ["s", "5"], ["b", "8"], ["m", "n"]] then becomes
vector<vector<char>> exch_symbols(list_of(vector<char>(list_of('i')('1')('l')))(vector<char>(list_of('s')('5'))(list_of('m')('n'))) (not tested and never used it with nested vectors, but it should be something like this)
For your real question of...
how could I translate L = [A, [B],
[[C], D]]] to C++ ... at all!
There is no direct translation - you've switched from storing values of the same type to storing values of variable type. Python allows this because it's a dynamically typed language, not because it has a nicer array syntax.
There are ways to replicate the behaviour in C++ (e.g. a vector of boost::any or boost::variant, or a user defined container class that supports this behviour), but it's never going to be as easy as it is in Python.
Your code:
vector<char> vector_1il;
vector_1il.push_back('1');
vector_1il.push_back('i');
vector_1il.push_back('l');
Concise code:
char values[] = "1il";
vector<char> vector_1il(&values[0], &values[3]);
Is it fine with you?
If you want to use std::string as suggested by Nim, then you can use even this:
//Concise form of what Nim suggested!
std::string s[] = {"1il", "5s", "8b", "mn"};
vector<std::string> exch_symbols(&s[0], &s[4]);
Rest you can follow Nim's post. :-)
In c++0x the instruction
vector<string> EXCH_SYMBOL_SETS={"i1l", "s5", "b8", "mn"} ;
compiles and works fine. Sadly enough the apparently similar statement
vector<vector<char>> EXCH_SYMBOL_SETS={{'i','1','l'},{'s','5'}, {'b','8'}, {'m','n'}};
doesn't work :-(.
This is implemented in g++ 4.5.0 or later you should add the -std=c++0x option. I think this feature is not yet avaliable in microsoft c (VC10), and I don't know what's the status of other compilers.
I know that this is an old post, but in case anyone stumbles across it, C++ has gotten MUCH better at dealing with this stuff:
In c++11 the first code block can simply be re-written in as:
std::vector<std::string> exch_symbols {"1il", "5s", "8b", "mn"};
This isn't special to string either, we can nest vector like so:
std::vector<std::vector<int>> vov {{1,2,3}, {2,3,5,7,11}};
And here's the entire code in c++14-style, with an added cout at the end:
#include <iostream>
#include <map>
#include <string>
#include <vector>
void add_all_char_pairs (std::multimap<char, char> & mmap, const std::string & str)
{
// we choose not to add {str[i], str[i]} pairs for some reason...
const int s = str.size();
for (int i1 = 0; i1 < s; ++i1)
{
char c1 = str[i1];
for (int i2 = i1 + 1; i2 < s; ++i2)
{
char c2 = str[i2];
mmap.insert({c1, c2});
mmap.insert({c2, c1});
}
}
}
auto all_char_pairs_of_each_str (const std::vector<std::string> & strs)
{
std::multimap<char, char> mmap;
for (auto & str : strs)
{
add_all_char_pairs(mmap, str);
}
return mmap;
}
int main ()
{
std::vector<std::string> exch_symbols {"1il", "5s", "8b", "mn"};
auto mmap = all_char_pairs_of_each_str(exch_symbols);
for (auto e : mmap)
{
std::cout << e.first << e.second << std::endl;
}
}