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
Why Are Unnamed Namespaces Used and What Are Their Benefits
How to Sort a Vector of Pairs Based on the Second Element of the Pair
How to Use a Custom Deleter With a Std::Unique_Ptr Member
G++ Undefined Reference to Typeinfo
Int A[] = {1,2,}; Why Is a Trailing Comma in an Initializer-List Allowed
Visual Studio Code, #Include ≪Stdio.H≫ Saying "Add Include Path to Settings"
Why Can't I Compile an Unordered_Map With a Pair as Key
How to Output a Character as an Integer Through Cout
How Could Pairing New[] With Delete Possibly Lead to Memory Leak Only
Comparing a Variable to a Range of Values
No Matching Function - Ifstream Open()
Writing Your Own Stl Container
How to Output Coloured Text to a Linux Terminal
Undefined Behavior and Sequence Points Reloaded
C++ Syntax For Explicit Specialization of a Template Function in a Template Class