c++iterator

Zip several std::list iterators together


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 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 there in the input until runtime.


Solution

  • 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