What's the Best Way to Iterate Over Two or More Containers Simultaneously

Is there a way to iterate over two containers without using two for loops

Using range-v3, your go-to for all things range-related in C++17 or earlier:

for (int i : view::concat(a, b)) {
std::cout << i << ' ';
}

An elegant and fast way to consecutively iterate over two or more containers in Python?

Depending on what order you want to process the items:

import itertools

for items in itertools.izip(deque1, deque2, deque3):
for item in items:
some_action(item)

for item in itertools.chain(deque1, deque2, deque3):
some_action(item)

I'd recommend doing this to avoid hard-coding the actual deques or number of deques:

deques = [deque1, deque2, deque3]
for item in itertools.chain(*deques):
some_action(item)

To demonstrate the difference in order of the above methods:

>>> a = range(5)
>>> b = range(5)
>>> c = range(5)
>>> d = [a, b, c]
>>>
>>> for items in itertools.izip(*d):
... for item in items:
... print item,
...
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
>>>
>>> for item in itertools.chain(*d):
... print item,
...
0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
>>>

Range for loop with multiple containers

In other (often functional) languages this is done by using a function called zip. As an example, Python has a builtin zip that iterates over over its arguments and returns a tuple:

for i in zip( [1,2,3], (1,2,3), { 0:0, 1:1, 2:2 } ): 
l,t,d = i
print("list item: %d, tuple item %d, dict item %d" % (l,t,d) )

You can use a range library in C++ to get that functionality, e.g. Boost.Range or Eric Niebler's rangev3. Ranges were unfortunately not voted in the C++17 standard, but I would never start a project without a range library. In Boost.Range the function is called combine:

#include <boost/range/combine.hpp>
#include <boost/tuple/tuple.hpp>
#include <iostream>
#include <vector>
#include <list>

int main(int, const char*[])
{
using namespace boost;

std::vector<int> const v{0,1,2,3,4};
std::list<char> const l{'a', 'b', 'c', 'd', 'e'};

for(auto const& i: combine(v, l))
{
int ti;
char tc;
boost::tie(ti,tc) = i;
std::cout << '(' << ti << ',' << tc << ')' << '\n';
}

return 0;
}

With C++17 you can replace the std::tie with structured binding and remove the kind of unusual "initialization" with std::tie.

  for(auto const& [ti,tc] : boost::combine(v, l)) {
std::cout << '(' << ti << ',' << tv << ')' << '\n';
}

While I regret that ranges are not included in C++17, I think that structured bindings are a great advancement and will seriously change the way code is written. Having ranges in the standard would make them more popular and elevate them from a third-party library where many people have objections because it is something they don't know to a standard feature that C++ programmer ought to know.

Iterating over more than one seq. container in C++11

The range-based for loop was designed as a convenience for iterating one range, because it's by far the most common case. If you need to iterate multiple ranges, which is not that most common case, you can still do it the traditional way:

for (auto i1 = begin(v1), i2 = begin(v2), e = end(v1); i1 != e; ++i1, ++i2)
{
// processing
}

Easy way to iterate over two structures consecutively with a foreach loop

Use views::concat from range-v3:

for (uint32_t number : views::concat(first, second)) {
<DO SOMETHING WITH number>
}

This only works with ranges that have the same underlying type (which is true in this example).


In Boost (i.e. range-v2), this is spelled boost::range::join(first, second).

Since it's not apparent from the Boost documentation directly, this function is found in boost/range/join.hpp

How can I iterate over two vectors simultaneously using BOOST_FOREACH?

Iterating over two things simultaneously is called a "zip" (from functional programming), and Boost has a zip iterator:

The zip iterator provides the ability to parallel-iterate over several
controlled sequences simultaneously. A zip iterator is constructed
from a tuple of iterators. Moving the zip iterator moves all the
iterators in parallel. Dereferencing the zip iterator returns a tuple
that contains the results of dereferencing the individual iterators.

Note that it's an iterator, not a range, so to use BOOST_FOREACH you're going to have to stuff two of them into an iterator_range or pair. So it won't be pretty, but with a bit of care you can probably come up with a simple zip_range and write:

BOOST_FOREACH(boost::tuple<int,int> &p, zip_range(v1, v2)) {
doSomething(p.get<0>(), p.get<1>());
}

Or special-case for 2 and use std::pair rather than boost::tuple.

I suppose that since doSomething might have parameters (int&, int&), actually we want a tuple<int&,int&>. Hope it works.

Iterating std::vectors simultaneously using standard C++

You can use good old index:

auto size = std::min( answers.size(), count.size() ); // or at least assert that size is equal
for( size_t i = 0; i < size; ++i ) {
const auto &a = answers[i];
const auto c = count[i];
// .. same as before

note this way you possibly avoiding to make 2 copies of std::string per iteration - answers -> tuple -> a



Related Topics



Leave a reply



Submit