Split vector to multiple array/vector C++ - c++

I have problem to split a string vector to smaller integer vector\array. My input vector data looks like:
std::vector<std::string> v(2);
v[0] = "0 14 150";
v[1] = "1 2 220";
//...
I know one solution, to make three arrays and to use sstream to convert data to integer. But i want to avoid making "spaghetti" code.
Thank you,
Peter.

I found a split function at stackoverflow some time ago. Unfortunatly, I cannot post the link anymore.
void split(const std::string & str, std::vector<std::string>& cont, const std::string & delims)
{
std::size_t current, previous = 0;
current = str.find_first_of(delims);
while (current != std::string::npos)
{
cont.push_back(std::move(str.substr(previous, current - previous)));
previous = current + 1;
current = str.find_first_of(delims, previous);
}
cont.push_back(std::move(str.substr(previous, current - previous)));
}
I will need delimiter in your strings (seems to be backspace in your case) and call the function on each element of your string vector:
int main()
{
std::vector<std::string> vec{ "0 14 150","1 2 220" };
std::vector<std::vector<int>> intVec(3,std::vector<int>(vec.size()));
for (int i = 0; i < vec.size(); i++)
{
std::vector<std::string> singleStr;
split(vec[i], singleStr, " ");
for (int j=0; j < singleStr.size();j++)
intVec[j][i] = (std::stoi(singleStr[j]));
}
system("pause");
}
A more generic solution could look like this. You can add further types to BasicVariant
#include <string>
#include <vector>
class BasicVariant
{
private:
std::string str;
public:
BasicVariant(const std::string& _str) :str(_str) {}
BasicVariant(int value) :str(std::to_string(value)) {}
BasicVariant(double value) :str(std::to_string(value)) {}
inline int toInt()const { return *this; }
inline double toDouble()const { return *this; }
inline std::string toString()const { return *this; }
inline bool toBool()const { return toDouble(); }
inline operator int()const { return std::stoi(str); }
inline operator double()const { return std::stof(str); }
inline operator std::string()const { return str; }
inline operator bool()const { return toDouble(); }
};
template<typename T>
void split(const std::string& str, std::vector<T>& sink, const std::string& delims)
{
std::size_t current, previous = 0;
current = str.find_first_of(delims);
while (current != std::string::npos)
{
sink.push_back(std::move(BasicVariant(str.substr(previous, current - previous))));
previous = current + 1;
current = str.find_first_of(delims, previous);
}
sink.push_back(std::move(BasicVariant(str.substr(previous, current - previous))));
}
int main()
{
std::vector<std::string> vec{ "0 14 150","1 2 220" };
std::vector<std::vector<int>> intVec(3, std::vector<int>(vec.size()));
for (int i = 0; i < vec.size(); i++)
{
std::vector<int> row;
split(vec[i], row, " ");
for (int j = 0; j < row.size(); j++)
intVec[j][i] = row[j];
}
system("pause");
}

Edit: I removed a verbose transposing function.
I assume that you want to convert std::vector<std::string> to a 2D matrix std::vector<std::vector<int>>.
For instance, for your example, the desired result is assumed to be arr1 = {0,1,...}, arr2 = {14,2,...} and arr3 = {150,220,...}.
First,
We can use std::istream_iterator to extract integers from strings.
We can also apply the range constructor to create a std::vector<int> corresponding to each string.
So the following function would work for you and it does not seem to be a spaghetti code at least to me.
First, this function extract two integer arrays {0,14,150,...} and {1,2,220,...} as matrices from a passed string vector v.
Since a default constructed std::istream_iterator is an end-of-stream iterator, each range constructor reads each string until it fails to read the next value.
And finally, transposed one is returned:
#include <vector>
#include <string>
#include <sstream>
#include <iterator>
template <typename T>
auto extractNumbers(const std::vector<std::string>& v)
{
std::vector<std::vector<T>> extracted;
extracted.reserve(v.size());
for(auto& s : v)
{
std::stringstream ss(s);
std::istream_iterator<T> begin(ss), end; //defaulted end-of-stream iterator.
extracted.emplace_back(begin, end);
}
// this also validates following access to extracted[0].
if(extracted.empty()){
return extracted;
}
decltype(extracted) transposed(extracted[0].size());
for(std::size_t i=0; i<transposed.size(); ++i){
for(std::size_t j=0; j<extracted.size(); ++j){
transposed.at(i).push_back(std::move(extracted.at(j).at(i)));
}
}
return transposed;
}
Then you can extract integers from a string vector as follows:
DEMO
std::vector<std::string> v(n);
v[0] = "0 14 150";
v[1] = "1 2 220";
...
v[n-1] = "...";
auto matrix = extractNumbers<int>(v);
where matrix[0] is arr1, matrix[1] is arr2, and so on.
We can also quickly get internal pointers of them by auto arr1 = std::move(matrix[0]);.

We have here some misunderstands.
Output of my program should have three arrays/vectors.
The output looks like:
arr1| arr1| arr3
0 | 14 | 150
1 | 2 | 220
2 | 4 | 130

Related

How to create a large number of combinations lazily in C++

I want to create a combination of K elements one each from K sets. Each set can have n elements in it.
set1 = {a1, a2, a3}
set2 = {b1, b2, b3 , b4}
set3 = {c1, c2}
Required Combinations = {{a1,b1,c1}, {a1,b2,c1} ... {a3,b4,c2}}
Number of combinations = 3*4*2 =24
So if K is large and n is large we run into Out of Memory very quickly. Refer to the below code snippet how we are creating combinations today. If we create all the combinations in a case where K is relatively large, we go out of memory! So for instance, if K=20 and each set has 5 elements, the combinations are 5^20, which is extremely large in memory. So I want an alternative algorithm where I don't need to store all those combinations in memory all at a time before I start consuming the combinations.
vector<vector<string>> setsToCombine;
vector<vector<string>> allCombinations;
vector<vector<string>> *current =
new vector<vector<string>>{vector<string>()};
vector<vector<string>> *next = new vector<vector<string>>();
vector<vector<string>> *temp;
for (const auto& oneSet : setsToCombine) {
for (auto& cur : *current) {
for (auto& oneEle : oneSet) {
cur.push_back(oneEle);
next->push_back(cur);
cur.pop_back();
}
}
temp = current;
current = next;
next = temp;
next->clear();
}
for (const auto& cur : *current) {
allCombinations.push_back(cur);
}
current->clear();
next->clear();
delete current;
delete next;
You can store the indexes and lazely iterate over the combinations
#include <cstdint>
#include <iostream>
#include <vector>
using v_size_type = std::vector<int>::size_type;
using vv_size_type = std::vector<v_size_type>::size_type;
bool increment(std::vector<v_size_type> &counters, std::vector<v_size_type> &ranges) {
for (auto idx = counters.size(); idx > 0; --idx) {
++counters[idx - 1];
if (counters[idx - 1] == ranges[idx - 1]) counters[idx - 1] = 0;
else return true;
}
return false;
}
std::vector<int> get(const std::vector<std::vector<int>> &sets, const std::vector<v_size_type> &counters) {
std::vector<int> result(sets.size());
for (vv_size_type idx = 0; idx < counters.size(); ++idx) {
result[idx] = sets[idx][counters[idx]];
}
return result;
}
void print(const std::vector<int> &result) {
for (const auto el : result) {
std::cout << el << ' ';
}
}
int main() {
const std::vector<std::vector<int>> sets = {{-5, 2}, {-100, -21, 0, 15, 32}, {1, 2, 3}};
std::vector<v_size_type> ranges(sets.size());
for (vv_size_type idx = 0; idx < sets.size(); ++idx) {
ranges[idx] = sets[idx].size();
}
std::vector<v_size_type> counters(sets.size());
while (true) {
print(get(sets, counters));
std::cout << '\n';
if (!increment(counters, ranges)) break;
}
}
Godbolt
You can also use the odometer approach.
First, let us look again, what an odometer is. It looks like this:
There are several disks, with values printed on it. And if the odometer runs forward, it will show the Cartesian product of all values on the disks.
That is somehow clear, but how to use this principle? The solution is, that each set of values will be a disk, and the values of the set, will be put on the corresponding disk. With that, we will have an odometer, where the number of values on each disk is different. But this does not matter.
Also here, if a disks overflows, the next disk is incremented. Same principle like a standard odometer. Just with maybe more or less values.
And, you can put everything on a disk, not just integers. This approach will work always.
We can abstract a disk as a std::vector of your desired type. And the odometer is a std::vector of disks.
All this we can design in a class. And if we add iterator functionality to the class, we can easily handle it.
In the example below, I show only a minimum set of functions. You can add as many useful functions to this class as you like and tailor it to your needs.
The object oriented approach is often better to understand in the end.
Please check:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <initializer_list>
#include <algorithm>
#include <iterator>
using MyType = int;
using Disk = std::vector<MyType>;
using Disks = std::vector<Disk>;
// Abstraction for a very simple odometer
class Odometer {
Disks disks{};
public:
// We will do nearly everything with the iterator of the odometer class
struct iterator {
// Definitions for iterator ----------------
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::vector<MyType>;
using pointer = std::vector<MyType>*;
using reference = std::vector<MyType>&;
const Disks& d; // Reference to disks from super class
int overflow{}; // Indicates an overflow of all disks
std::vector<std::size_t>positions{}; // Stores position of any disks
// Iterator constructor
iterator(const Disks& dd, const int over = 0) : d(dd), overflow(over) {
positions = std::vector<std::size_t>(dd.size(), 0);
}
// Dereference iterator
value_type operator*() const {
std::vector<MyType> result(d.size());
for (std::size_t i{}; i < d.size(); ++i) result[i] = d[i][positions[i]];
return result;
};
// Comparison
bool operator != (const iterator& other) { return positions != other.positions or overflow != other.overflow; }
// And increment the iterator
iterator operator++() {
int carry = 0; std::size_t i{};
for (i=0; i < d.size(); ++i) {
if (positions[i] >= d[i].size() - 1) {
positions[i] = 0;
carry = 1;
}
else {
++positions[i];
carry = 0;
break;
}
}
overflow = (i == d.size() and carry) ? 1 : 0;
return *this;
}
};
// Begin and End functions. End is true, if there is a flip over of all disks
iterator begin() const { return iterator(disks); }
iterator end() const { return iterator(disks, 1); }
// Constructors
Odometer() {}; // Default (useless for this example)
// Construct from 2d initializer list
Odometer(const std::initializer_list<const std::initializer_list<MyType>> iil) {
for (const std::initializer_list<MyType>& il : iil) {
disks.push_back(il);
}
}
// Variadic. Parameter pack and fold expression
template <typename ... Args>
Odometer(Args&&... args) {
(disks.push_back(std::forward<Args>(args)), ...);
}
// Simple output of everything
friend std::ostream& operator << (std::ostream& os, const Odometer& o) {
for (const auto vi : o) {
for (const MyType i : vi) os << i << ' ';
os << '\n';
}
return os;
}
};
// Some test
int main() {
// Define Odometer. Initialiaze wit normal initializer list
Odometer odo1{ {1,2},{3},{4,5,6} };
// Show complete output
std::cout << odo1 << "\n\n\n";
// Create additional 3 vectors for building a new cartesian product
std::vector<MyType> v1{ 1,2 };
std::vector<MyType> v2{ 3,4 };
std::vector<MyType> v3{ 5,6 };
// Define next Odometer and initialize with variadic constructor
Odometer odo2(v1, v2, v3);
// Use range based for loop for output
for (const std::vector<MyType>& vm : odo2) {
for (const MyType i : vm) std::cout << i << ' ';
std::cout << '\n';
}
}

how to read data from text file and store it into array in c++

I am trying to read the data from a text file and store it into array. I need it for solving FEM problem. Let's say my text file is as follows:
node: 1,2,3,4,5,6,7,8,9,10
x: 4,4,3.75,3.76773151,3,3.59192947,4,3.5,3.55115372,3.375, 3.71330586
y: 3,275,3,2.65921885,2.79192947,2.5,3,2.55115372,2.78349365,2.36222989
z: 0,0,0,0,0,0,0,0,0,0
I want to store this data from text file into a 10*4 matrix (myarray[10][4]). Also I need to store each column of this array into a vector. Let's say my vectors are:
double x[10];
double y[10];
double z[10];
for (int i = 0; i < 10; i++)
{
x[i] = myarray[i][1];
y[i] = myarray[i][2];
z[i] = myarray[i][3];
}
I wrote the code like this:
int main()
{
string line;
string coordinate[10][4];
ifstream mesh("mesh.txt");
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < 4; ++j)
{
if (getline(mesh, line, ';'))
{
coordinate[i][j] = line;
cout << coordinate[i][j] << endl;
cout << "mesh : " << line[0] << endl;
}
}
}
mesh.close();
}
Now my problem is when I want to put the each column of coordinate into a vector I get this error:
no suitable conversion function from string to double exist
I don't understand this error, and need help fixing it.
An iterative way may like this, use the splitter in a customized iterator class. It seems to be complicated but I think it's easy to maintain.
Note that the iterator class is a modified version of this great answer.
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <vector>
template <typename T>
class istream_line_iterator : public std::iterator<std::input_iterator_tag, T> {
std::istream* stream;
public:
// Creating from a stream or the end iterator.
istream_line_iterator(std::istream& s) : stream(&s) { dropLeadingSpace(); }
istream_line_iterator() : stream(nullptr) {}
// Copy
istream_line_iterator(istream_line_iterator const& copy)
: stream(copy.stream) {}
istream_line_iterator& operator=(istream_line_iterator const& copy) {
stream = copy.stream;
return *this;
}
// The only valid comparison is against the end() iterator.
// All other iterator comparisons return false.
bool operator==(istream_line_iterator const& rhs) const {
return stream == nullptr && rhs.stream == nullptr;
}
bool operator!=(istream_line_iterator const& rhs) const {
return !(*this == rhs);
}
// Geting the value modifies the stream and returns the value.
// Note: Reading from the end() iterator is undefined behavior.
T operator*() const {
T value;
(*stream) >> value;
return value;
}
T* operator->() const; // Not sure I want to implement this.
// Input streams are funny.
// Does not matter if you do a pre or post increment. The underlying stream
// has changed. So the effect is the same.
istream_line_iterator& operator++() {
dropLeadingSpace();
return *this;
}
istream_line_iterator& operator++(int) {
dropLeadingSpace();
return *this;
}
private:
void dropLeadingSpace() {
// Only called from constructor and ++ operator.
// Note calling this on end iterator is undefined behavior.
char c;
while ((*stream) >> std::noskipws >> c) {
if (c == '\n') {
// End of line. So mark the iterator as reaching end.
stream = nullptr;
return;
}
if (!std::isspace(c) && c != ',') {
// Found a non space character so put it back
stream->putback(c);
return;
}
}
// End of stream. Mark the iterator as reaching the end.
stream = nullptr;
}
};
int main() {
std::ifstream ifs("1.in");
std::string line;
std::vector<std::vector<double>> vec;
while (std::getline(ifs, line)) {
std::istringstream iss(line);
std::string pre;
iss >> pre;
std::vector<double> line_vec;
auto beg = istream_line_iterator<double>(iss);
auto end = istream_line_iterator<double>();
std::copy(beg, end, std::back_inserter(line_vec));
vec.push_back(std::move(line_vec));
}
for (const auto& inner : vec) {
for (auto d : inner) {
std::cout << d << ' ';
}
std::cout << std::endl;
}
return 0;
}

C++ best way to split vector into n vector

I have a std::vector<std::string> in this vector I push_back string from txt file, like this :
std::string line;
std::vector<std::string> path;
while(getline(fichier, line))
{
path.push_back(line);
}
I would like to split path vector into n other vector of 10 line for example.
So if size of my vector is 25, I want 2 other vector of 10 element and one vector of 5 element.
What is the best way to do that ?
Best is a matter of opinion, but you could do something like the following (with bunch_size being 10):
for(size_t i = 0; i < strings.size(); i += bunch_size) {
auto last = std::min(strings.size(), i + bunch_size);
bunches.emplace_back(strings.begin() + i, strings.begin() + last);
}
demo
If your strings are large and you want to avoid copying, you can go with the move version:
for(size_t i = 0; i < strings.size(); i += bunch_size) {
auto last = std::min(strings.size(), i + bunch_size);
auto index = i / bunch_size;
auto& vec = bunches[index];
vec.reserve(last - i);
move(strings.begin() + i, strings.begin() + last, back_inserter(vec));
}
demo
I propose something quite general (it works with different containers and different types, the complexity will change in that case):
#include <algorithm>
#include <iterator>
#include <vector>
template<typename Vector>
auto split_vector(const Vector& v, unsigned number_lines) {
using Iterator = typename Vector::const_iterator;
std::vector<Vector> rtn;
Iterator it = v.cbegin();
const Iterator end = v.cend();
while (it != end) {
Vector v;
std::back_insert_iterator<Vector> inserter(v);
const auto num_to_copy = std::min(static_cast<unsigned>(
std::distance(it, end)), number_lines);
std::copy(it, it + num_to_copy, inserter);
rtn.push_back(std::move(v));
std::advance(it, num_to_copy);
}
return rtn;
}
You can specify the number of lines you want to split:
For example:
int main(int argc, char *argv[]) {
std::vector<std::string> input_vector = {"First", "Second", "Third"};
auto vs = split_vector(input_vector, 2);
return 0;
}
It will produce two vectors: {"First", "Second"} and {"Third"}.
You may use stream iterators to do the job while reading the file:
using packet_t = Packet<5>;
using filler_t = std::istream_iterator<packet_t>;
std::vector<packet_t> packets{
filler_t(stream),
filler_t()
};
With the structure Packet declaring needed operator>>:
template<size_t size>
struct Packet
{
std::vector<std::string> lines;
friend std::istream& operator>>(std::istream& is, Packet& packet)
{
packet.lines.clear();
std::string line;
for(size_t i = 0; i < size && std::getline(is, line); ++i)
{
packet.lines.push_back(line);
}
if(packet.lines.size() > 0)
{
is.clear();
}
return is;
}
};
Note that the stream is cleared when the packet is not empty for the last lines.
Complete code:
#include <iostream>
#include <iterator>
#include <sstream>
#include <vector>
template<size_t size>
struct Packet
{
std::vector<std::string> lines;
friend std::istream& operator>>(std::istream& is, Packet& packet)
{
packet.lines.clear();
std::string line;
for(size_t i = 0; i < size && std::getline(is, line); ++i)
{
packet.lines.push_back(line);
}
if(packet.lines.size() > 0)
{
is.clear();
}
return is;
}
};
int main()
{
std::istringstream stream("1\n2\n3\n4\n5\n6\n7\n");
using packet_t = Packet<5>;
using filler_t = std::istream_iterator<packet_t>;
std::vector<packet_t> packets{
filler_t(stream),
filler_t()
};
for(auto& packet : packets)
{
for(auto& line : packet.lines)
{
std::cout << line << " ";
}
std::cout << std::endl;
}
}

Convert a set to a vector of vector

I'm new to C++ and I'm trying to convert unordered_set<string> to vector<vector<int>>
The set contains ("1,2,2","1","1,2","2","2,2"), and each element is a string.
I would like to output a vector<vector<int>> containing
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
So how would I parse each element (string) and make it looks like the above scheme?
#define SSTR( x ) dynamic_cast< std::ostringstream & >( \
( std::ostringstream() << std::dec << x ) ).str()
vector<vector<int> > subsetsWithDup(const vector<int> &num) {
unordered_set<string> result;
for (int i = 0; i < num.size(); i++)
{
result.insert(SSTR(num[i]));
for (int j = i+1; j < num.size(); j++)
{
string d = SSTR(num[i]) + "," +SSTR(num[j]);
result.insert(d);
}
}
string lastString= "";
for (int i = 0; i < num.size(); i++)
{
if ( i == num.size() -1)
{
lastString+= SSTR(num[i]);
}
else
{
lastString+= SSTR(num[i])+",";
}
}
result.insert(lastString);
// convert result back to vector<vector<int>>
return result;
}
If you don't need to validate your strings you can do a transform to construct the vector<vector<int>>:
set<string> foo{ "1,2,2", "1", "1,2", "2", "2,2" };
vector<vector<int>> bar(foo.size());
transform(foo.begin(), foo.end(), bar.begin(), [](const string& i){
vector<int> result;
auto it = const_cast<char*>(i.c_str());
for (result.push_back(static_cast<int>(strtol(it, &it, 10)));
it < i.c_str() + i.size();
result.push_back(static_cast<int>(strtol(++it, &it, 10))));
return result;
});
The lambda in the transform will step through the string:
Start it at the beginning of the string
Using strtol to extract each number
Pushing each number into result
Stepping over each ',' with ++it
Return the constructed result
Prerequisites: This lambda assumes that your input set doesn't contain:
An empty string
A string that begins or ends with a comma (as in "1,2,")
A string that has consecutive commas (as in "1,,2")
That the string contains only contains digits and commas
Note: As a general rule a const_cast is bad so I wanted to comment on why I'm making one. Note that *it is never written to, only it is written to. So this is not violating the const-ness of const string& i. An alternative to strtol that doesn't require a const_cast is stoi, but until we get string_view from C++14 we'll need to construct a new string at each comma, so that's dreadfully inefficient.
One of the possible solution can be:
vector<string> split (string str, string seq) {
vector<string> ret {};
size_t pos {};
while ((pos = str.find (seq)) != string::npos) {
ret.push_back (str.substr (0, pos));
str = str.substr (pos+seq.size ());
}
ret.push_back (str);
return ret;
}
vector<int> to_vec_int (vector<string>&& vec) {
vector<int> ret {};
for (const auto& v : vec) {
ret.push_back (stoi (v));
}
return ret;
}
int main () {
unordered_set<string> st {"1,2,2","1","1,2","2","2,2"};
vector<vector<int>> vec {};
for (const auto& s : st) {
vec.push_back (to_vec_int (split (s, ",")));
}
for (const auto& v : vec) {
for (const auto& s : v) {
cout << s << " ";
}
cout << endl;
}
return 0;
}
Since your elements are delimited by comma, we can split them using split () function and we get vector of strings. This vector has to be converted to vector of int, which is the reason of existence of to_vec_int ().
Here is another possible solution using istringstream to find the commas:
#include <iostream>
#include <string>
#include <vector>
#include <unordered_set>
#include <sstream>
void main()
{
using namespace std;
unordered_set<string> strSet;
strSet.insert("1,2,2");
strSet.insert("1");
strSet.insert("1,2");
strSet.insert("2");
strSet.insert("2,2");
vector<int> nums;
vector<vector<int>> arr_of_nums;
for (const auto &str : strSet) {
istringstream strStream(str);
string strToInt;
while (getline(strStream, strToInt, ',')) {
nums.push_back(stoi(strToInt));
}
arr_of_nums.push_back(nums);
nums.clear();
}
for(const auto &nums : arr_of_nums) {
for (const auto &num : nums) {
cout << num << ",";
}
cout << endl;
}
}

Combination of lists by type algorithm

I'm attempting to create an algorithm in C++ which will give me all of the possible combinations of a set of list items (input in a map format). I want to avoid duplicates and make sure to cover all possible combinations. To simplify the example, here's what the input may look like:
map<string, vector<string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
I'd feed this map into the algorithm, and it should spit out a vector with all of the possible combinations (using one of each key type):
wheat+ham+lettuce+mustard
wheat+turkey+lettuce+mustard
wheat+roastbeef+lettuce+mustard
white+ham+lettuce+mustard
white+turkey+lettuce+mustard
white+roastbeef+lettuce+mustard
It needs to work for any map of string vectors. So far I've tried and gotten close, but I end up with duplicate combinations and missed combinations:
sandwichList getCombinations(sandwichMap sMap)
{
locList retList;
int totalCombos = 1;
for (sandwichMapIt i = sMap.begin(); i != sMap.end(); ++i)
{
totalCombos *= i->second.size();
}
retList.resize(totalCombos);
int locCount;
for (sandwichMapIt a = sMap.begin(); a != sMap.end(); ++a)
{
locCount = 0;
for (locListIt l = a->second.begin(); l != a->second.end(); ++l)
{
for (unsigned int i = 0; i < totalCombos / a->second.size(); ++i)
{
retList[i + a->second.size() * locCount] += *l;
}
locCount++;
}
}
return retList;
}
Any help would be greatly appreciated!
Updated code:
#include <vector>
#include <map>
#include <list>
#include <iostream>
typedef std::vector<std::string> strVec;
typedef std::list<std::string> strList;
typedef std::map<std::string, strVec> sandwichMap;
int main()
{
sandwichMap sMap;
sMap["bread"].push_back("wheat");
sMap["bread"].push_back("white");
sMap["meat"].push_back("ham");
sMap["meat"].push_back("turkey");
sMap["meat"].push_back("roastbeef");
sMap["veggie"].push_back("lettuce");
sMap["sauce"].push_back("mustard");
strList finalSandwichList;
for (sandwichMap::iterator i = sMap.begin(); i != sMap.end(); ++i)
{
strList tmpSandwich;
for (strVec::iterator j = i->second.begin(); j != i->second.end(); ++j)
{
if (finalSandwichList.empty())
{
tmpSandwich.push_back(*j);
}
else
{
for (strList::iterator k = finalSandwichList.begin(); k != finalSandwichList.end(); ++k)
tmpSandwich.push_back(*k + "+" + *j);
}
}
tmpSandwich.swap(finalSandwichList);
}
for (strList::iterator i = finalSandwichList.begin(); i != finalSandwichList.end(); ++i)
{
std::cout << *i << std::endl;
}
return 0;
}
//solution
std::list<std::string> result;
for(auto i=sandwichMap.begin(); i!=sandwichMap.end(); ++i) {
std::list<std::string> new_result;
for(auto j=i->second.begin(); j!=i->second.end(); ++j) {
if(result.empty())
new_result.push_back(*j);
else
for(auto k=result.begin(); k!=result.end(); ++k)
new_result.push_back(*k + "+" + *j);
}
new_result.swap(result);
}
This should work :
#include<iostream>
#include<map>
#include<string>
#include<algorithm>
using namespace std;
map<string, vector<string>> sMap;
vector<string> add;
int sett[200], countt;
void solve(map<string, vector<string>>::iterator itt, int ct, vector<string> addd){
vector<string> tmp = itt->second;
if(ct == countt){
for(int j=0;j<addd.size();j++){
cout<<addd[j]<<" ";
}
cout<<endl;
return;
}
itt++;
for(int i=0;i<tmp.size();i++){
//cout<<tmp[i]<<" ";
addd.push_back(tmp[i]);
solve(itt, ct+1, addd);
vector<string>::iterator tempIt = addd.end();
addd.erase(tempIt--);
}
}
int main(){
sMap["bre"].push_back("wh");
sMap["bre"].push_back("whi");
sMap["me"].push_back("ham");
sMap["me"].push_back("tur");
sMap["me"].push_back("rr");
sMap["veg"].push_back("let");
sMap["sau"].push_back("mus");
countt = sMap.size();
solve(sMap.begin(), 0, add);
return 0;
}
I have used backtracking to evaluate every possible combination.
Note : it is in c++11 you might need to change some part of the code for lower version of c++
link to output : http://ideone.com/Ou2411
The code is kinda long because of the helper methods, but it does the job:
#include <vector>
#include <string>
#include <map>
#include <iostream>
using namespace std;
template <class T>
vector<T> Head(const vector<T> &v) {
return vector<T>(v.begin(), v.begin() + 1);
}
template <class T>
vector<T> Tail(const vector<T> &v) {
auto first = v.begin() + 1;
auto last = v.end();
return vector<T>(first, last);
}
template <class T>
vector<T> Concat(const vector<T> &v1, const vector<T> &v2) {
vector<T> result = v1;
result.insert(result.end(), v2.begin(), v2.end());
return result;
}
vector<vector<string>> CombineVectorWithScalar(const vector<vector<string>> &v, const string &scalar) {
vector<vector<string>> result = v;
for (unsigned i = 0; i < v.size(); i++) {
result[i].push_back(scalar);
}
return result;
}
vector<vector<string>> CombineVectorWithVector(const vector<vector<string>> &v1, const vector<string> &v2) {
if (v2.empty()) {
return vector<vector<string>>();
}
else {
auto headCombination = CombineVectorWithScalar(v1, v2.front());
auto tailCombination = CombineVectorWithVector(v1, Tail(v2));
return Concat(headCombination, tailCombination);
}
}
vector<string> GetKeys(const map<string, vector<string>> &mp) {
vector<string> keys;
for (auto it = mp.begin(); it != mp.end(); ++it) {
keys.push_back(it->first);
}
return keys;
}
vector<vector<string>> CombineMapValues(const map<string, vector<string>> &mp) {
vector<string> keys = GetKeys(mp);
vector<vector<string>> result;
auto &firstVector = mp.begin()->second;
for (auto it = firstVector.begin(); it != firstVector.end(); ++it) {
vector<string> oneElementList;
oneElementList.push_back(*it);
result.push_back(oneElementList);
}
vector<string> restOfTheKeys = Tail(keys);
for (auto it = restOfTheKeys.begin(); it != restOfTheKeys.end(); ++it) {
auto &currentVector = mp.find(*it)->second;
result = CombineVectorWithVector(result, currentVector);
}
return result;
}
void PrintCombinations(const vector<vector<string>> & allCombinations) {
for (auto it = allCombinations.begin(); it != allCombinations.end(); ++it) {
auto currentCombination = *it;
for (auto itInner = currentCombination.begin(); itInner != currentCombination.end(); ++itInner) {
cout << *itInner << " ";
}
cout << endl;
}
}
int main() {
map<string, vector<string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
auto allCombinations = CombineMapValues(sandwichMap);
PrintCombinations(allCombinations);
return 0;
}
void generate_all(std::map<std::string,std::vector<std::string>>::iterator start,
std::vector<std::string::iterator> accomulator,
std::map<std::string,std::vector<std::string>>& sMap){
for (auto it=start; it!=sMap.end(); ++it){
for (auto jt=it->second.begin(); jt!=it->second.end(); jt++){
generate_all(start+1,accomulator.pus_back[jt],sMap);
}
}
if (accomulator.size() == sMap.size()){
// print accomulator
}
}
Call with generate_all(sMap.begin(),aVector,sMap);
If the map is too big to go recursively, you can always generate an equivalent iterative code.
This solution is not recursive. Basically what it does is the following:
Compute how many combinations are actually possible
Know that for each key in the map, you're going to have to add nrCombinations/nrItemsInKey of them in total.
You can see it as a tree growing, branching more and more the more keys you have visited.
If you keep track of how many there are, how spaced they are and where they start you can automatically fill all combinations.
Code
#include <vector>
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, std::vector<std::string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
sandwichMap["sauce"].push_back("mayo");
// Compute just how many combinations there are
int combinationNr = 1;
for ( auto it : sandwichMap ) {
combinationNr *= it.second.size();
}
std::vector<std::vector<std::string>> solutions(combinationNr);
// We start with empty lists, thus we only have one cluster
int clusters = 1, clusterSize = combinationNr;
for ( auto category : sandwichMap ) {
int startIndex = 0;
int itemsNr = category.second.size();
int itemsPerCluster = clusterSize / itemsNr;
for ( auto item : category.second ) {
for ( int c = 0; c < clusters; ++c ) {
for ( int i = 0; i < itemsPerCluster; ++i ) {
// We sequentially fill each cluster with this item.
// Each fill starts offset by the quantity of items
// already added in the cluster.
solutions[startIndex+i+c*clusterSize].push_back(item);
}
}
startIndex += itemsPerCluster;
}
clusters *= itemsNr;
clusterSize = combinationNr / clusters;
}
for ( auto list : solutions ) {
for ( auto element : list ) {
std::cout << element << ", ";
}
std::cout << "\n";
}
return 0;
}