Using the boost library it is possible to zip together a known number of iterators using a zip iterator, but what about when the number of iterators to be zipped is not known until runtime?
To expand a little bit, I have a list of lists that are all the same size, and I need to group together all the values at each each index and feed them into another operation. Right now this is all manual, and I feel like there should be a better way.
Example:
Say I have 3 lists:
I need to transform these lists into:
I do not know how many lists are in the input until runtime.
Alright after spending almost 1/2 hour, I came up with this dynamic_zip_iterator
class which can be further improved, to make it look like STL-like iterators. As of now, it's very specific, as I've hardcoded std::list
in it which you can replace with std::vector
or can make even more generic:
Anyway, have a look at it:
template<typename T>
struct dynamic_zip_iterator
{
typedef typename std::list<T>::iterator list_iterator;
std::list<list_iterator> iterators;
std::list<std::list<T>> * plists;
dynamic_zip_iterator(std::list<std::list<T>> & lists, bool isbegin) : plists(&lists)
{
auto it = plists->begin();
for( ; it != plists->end(); ++it)
{
if ( isbegin )
iterators.push_back(it->begin());
else
iterators.push_back(it->end());
}
}
dynamic_zip_iterator(const dynamic_zip_iterator & zip) :
plists(zip.plists),iterators(zip.iterators) {}
dynamic_zip_iterator operator++()
{
auto it = iterators.begin();
for( ; it != iterators.end(); ++it)
++(*it);
return *this;
}
std::list<T> operator*()
{
std::list<T> lst;
auto it = iterators.begin();
for( ; it != iterators.end(); ++it)
lst.push_back(*(*it));
return lst;
}
bool operator!=(dynamic_zip_iterator &zip)
{
auto it1 = iterators.begin();
auto it2 = zip.iterators.begin();
return (*it1) != (*it2);
}
static dynamic_zip_iterator begin(std::list<std::list<T>> & lists)
{
return dynamic_zip_iterator<T>(lists, true);
}
static dynamic_zip_iterator end(std::list<std::list<T>> & lists)
{
return dynamic_zip_iterator<T>(lists, false);
}
};
Using it your problem reduces to this function:
std::list<std::list<int>> create_lists(std::list<std::list<int>>& lists)
{
std::list<std::list<int>> results;
auto begin = dynamic_zip_iterator<int>::begin(lists);
auto end = dynamic_zip_iterator<int>::end(lists);
for( ; begin != end ; ++begin)
{
results.push_back(*begin);
}
return results;
}
Test code:
int main() {
int a[] = {1, 2, 3, 4, 5}, b[] = {11, 12, 13, 14, 15}, c[] = {21, 22, 23, 24, 25};
std::list<int> l1(a,a+5), l2(b,b+5), l3(c,c+5);
std::list<std::list<int>> lists;
lists.push_back(l1);
lists.push_back(l2);
lists.push_back(l3);
std::list<std::list<int>> newlists = create_lists(lists);
for(auto lst = newlists.begin(); lst != newlists.end(); ++lst)
{
std::cout << "[";
std::copy(lst->begin(), lst->end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << "]" << std::endl;
}
return 0;
}
Output:
[1 11 21 ]
[2 12 22 ]
[3 13 23 ]
[4 14 24 ]
[5 15 25 ]
Online demo : http://ideone.com/3FJu1
I am pretty sure nothing exists today for that. But why not create a very simple list of iterator elements ? That would do the trick, i am sure !
Create a function for all 3 parts of the for
statement --> begin, end, increment
And that should be enough ! A bit more detail below.
begin : (const ref to list of lists, reference to empty list of iterators) --> construct the iterator list using begin() for each sublist
end : (const ref to list of iterators, const ref to list of lists) --> true if one iterator is at the end of its list
increment : (ref to list of iterators) --> increment every iterator in the list
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With