How to iterate two vectors at the same time in For LOOP

Hi all,

I have a issue where two different vectors has to iterate at same time.
Basically to say i have ZIP_Iteration problem with two vectors. I tried to analyse more about it through googling.

Need more information where i can get more.
1
2
3
4
5
6
7
8
9
10
for (int i = 0 ; i < number_of_loops ; ++i)
{
  // do something with iterator_one

  // do something with iterator_two

  // increment iterator_one if you want to

  // increment iterator_two if you want to
}
Here's a slight variation on Repeater's good advice.

Whenever possible, I prefer to put the "looping" logic within the for construct and the "process an item" logic in the body of the loop. When using two iterators, you can use the comma operator to set and increment both iterators:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

using std::cout;

int
main()
{
    int i;
    char c;
    for (i=1, c='a'; c < 'z'; ++i, ++c) {
	cout << "The " << i << "'th character is " << c << '\n';
    }

}

What you want is an up-and-coming feature of C++17 compliance that everyone is looking forward to. Currently you need a compiler that supports it, which are GCC 7.something and Clang 4:

 
  for (auto [x,y,z] : zip( xs, ys, zs )) ...

Of course, that is still fantasy code. You can get close, though, using boost:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>

// See https://stackoverflow.com/a/8513803/2706707
template <typename... Containers>
auto zip( Containers&&... containers ) 
  -> boost::iterator_range <boost::zip_iterator <decltype( boost::make_tuple( std::begin( containers )... ) )> >
{
  auto zip_begin = boost::make_zip_iterator( boost::make_tuple( std::begin( containers )... ) );
  auto zip_end   = boost::make_zip_iterator( boost::make_tuple( std::end(   containers )... ) );
  return boost::make_iterator_range( zip_begin, zip_end );
}

#include <iostream>
#include <set>
#include <vector>

int main()
{
  std::vector <int> xs   = { 2, 3, 5, 7, 11, 13 };
  int               ys[] = { 0, 1, 2, 3,  4,  5 };
  std::set    <int> zs     { 1, 4, 6, 7,  8,  9 };
  
  for (auto zi : zip( xs, ys, zs ))
  {
    auto [x, y, z] = zi;
    std::cout << "x, y, z = " << x << ", " << y << ", " << z << "\n";
  }
}

For code you can actually use, though, you will need some macro magic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>

template <typename... Containers>
auto zip( Containers&&... containers ) 
  -> boost::iterator_range <boost::zip_iterator <decltype( boost::make_tuple( std::begin( containers )... ) )> >
{
  auto zip_begin = boost::make_zip_iterator( boost::make_tuple( std::begin( containers )... ) );
  auto zip_end   = boost::make_zip_iterator( boost::make_tuple( std::end(   containers )... ) );
  return boost::make_iterator_range( zip_begin, zip_end );
}

#ifndef NUM_ARGS
#define NUM_ARGS0(X,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1, N, ...) N
#define NUM_ARGS(...) NUM_ARGS0(0, __VA_ARGS__, 20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
#endif

#define UNZIP_1(N,tuple,x)     auto x = boost::get <N-1> (tuple)
#define UNZIP_2(N,tuple,x,...) auto x = boost::get <N-2> (tuple); UNZIP_1(N,tuple,__VA_ARGS__)
#define UNZIP_3(N,tuple,x,...) auto x = boost::get <N-3> (tuple); UNZIP_2(N,tuple,__VA_ARGS__)
#define UNZIP_4(N,tuple,x,...) auto x = boost::get <N-4> (tuple); UNZIP_3(N,tuple,__VA_ARGS__)
#define UNZIP_5(N,tuple,x,...) auto x = boost::get <N-5> (tuple); UNZIP_4(N,tuple,__VA_ARGS__)
#define UNZIP_6(N,tuple,x,...) auto x = boost::get <N-6> (tuple); UNZIP_5(N,tuple,__VA_ARGS__)
#define UNZIP_7(N,tuple,x,...) auto x = boost::get <N-7> (tuple); UNZIP_6(N,tuple,__VA_ARGS__)
#define UNZIP_8(N,tuple,x,...) auto x = boost::get <N-8> (tuple); UNZIP_7(N,tuple,__VA_ARGS__)
#define UNZIP_9(N,tuple,x,...) auto x = boost::get <N-9> (tuple); UNZIP_8(N,tuple,__VA_ARGS__)

#define UNZIP1(N,tuple,...) UNZIP_ ## N(N,tuple,__VA_ARGS__)
#define UNZIP0(N,tuple,...) UNZIP1(N,tuple,__VA_ARGS__)
#define unzip(tuple,...) UNZIP0(NUM_ARGS(__VA_ARGS__),tuple,__VA_ARGS__)



#include <iostream>
#include <set>
#include <vector>

int main()
{
  std::vector <int> xs   = { 2, 3, 5, 7, 11, 13 };
  int               ys[] = { 0, 1, 2, 3,  4,  5 };
  std::set    <int> zs     { 1, 4, 6, 7,  8,  9 };
  
  for (auto zi : zip( xs, ys, zs ))
  {
    unzip( zi, x, &y, z );
    std::cout << "x, y, z = " << x << ", " << y << ", " << z << "\n";
    x+=1000;
    y+=1000;
  }
  for (auto x : xs) std::cout << "x = " << x << "\n";
  for (auto y : ys) std::cout << "y = " << y << "\n";
}

There is one important caveat in all this: all input sequences must be the same length!.
If not you invite undefined behavior.

I personally think it should be smart enough to handle that, but adding that smartness means having better than the unchecked boost zip_iterator currently provides. (Which means writing your own checked zip iterator. Alas.)

Of course, there is always the old school ways:

1
2
3
4
5
6
7
8
9
  // recommended
  auto xi = xs.begin();
  auto yi = ys.begin();
  while (xi != xs.end() and yi != ys.end())
  {
    auto  x = *xi++;
    auto& y = *yi++;
    ...
  }
1
2
3
4
5
6
  for (std::size_t n = 0; n < std::min( xs.size(), ys.size() ); n++)
  {
    auto  x = xs[ n ];
    auto& y = ys[ n ];
    ...
  }

Enjoy!
Topic archived. No new replies allowed.