What is the best way to iterate two or more containers simultaneously

C ++ 11 provides several ways to iterate over containers. For example:

Range Based Cycle

for(auto c : container) fun(c) 

stand :: for_each

 for_each(container.begin(),container.end(),fun) 

However, what is the recommended way to iterate over two (or more) containers of the same size to accomplish something like:

 for(unsigned i = 0; i < containerA.size(); ++i) { containerA[i] = containerB[i]; } 
+68
c ++ iterator c ++ 11 containers
Sep 23 '12 at 12:13
source share
9 answers

In your specific example, just use

 std::copy_n(contB.begin(), contA.size(), contA.begin()) 

For a more general case, you can use Boost.Iterator zip_iterator , with a small function to make it suitable for use in a loop for ranges. In most cases, this will work:

 template<class... Conts> auto zip_range(Conts&... conts) -> decltype(boost::make_iterator_range( boost::make_zip_iterator(boost::make_tuple(conts.begin()...)), boost::make_zip_iterator(boost::make_tuple(conts.end()...)))) { return {boost::make_zip_iterator(boost::make_tuple(conts.begin()...)), boost::make_zip_iterator(boost::make_tuple(conts.end()...))}; } // ... for(auto&& t : zip_range(contA, contB)) std::cout << t.get<0>() << " : " << t.get<1>() << "\n"; 

Real-time example.

However, for full-blown versatility, you probably want something more than this , which will work correctly for arrays and custom types that do not have a begin() / end() member but have begin / end functions in their namespace. In addition, it will allow the user to get const access through the zip_c... functions.

And if you are a supporter of good error messages, like me, then you probably want this , which checks if any of the temporary containers were passed to any of the zip_... functions and prints a good error message, if so .

+36
Sep 23 '12 at 15:04
source share

Rather late to the party. But: I would iterate over the indices. But not with the classic for loop, but instead with a for loop based on a range of indices:

 for(unsigned i : indices(containerA)) containerA[i] = containerB[i]; 

indices is a simple wrapper function that returns a (lazily evaluated) range for indices. Since the implementation - although simple - is too long to post here, you can find the implementation on GitHub .

This code is efficient using the manual classic for loop.

If this pattern is often found in your data, consider using another template, which zip consists of two sequences and creates a series of tuples corresponding to paired elements:

 for (auto items&& : zip(containerA, containerB)) get<0>(items) = get<1>(items); 

The zip implementation is left as an exercise for the reader, but it follows easily from the indices implementation.

+35
Sep 25 '13 at 13:19
source share

I wonder why no one mentioned this:

 auto ItA = VectorA.begin(); auto ItB = VectorB.begin(); while(ItA != VectorA.end() || ItB != VectorB.end()) { if(ItA != VectorA.end()) { ++ItA; } if(ItB != VectorB.end()) { ++ItB; } } 

PS: if the dimensions of the container do not match, then you have to put the code inside the if statements.

+19
Mar 28 '16 at 10:36
source share

There are many ways to do certain things with multiple containers, as indicated in the algorithm header. For example, in the example you specified, you can use std::copy instead of an explicit loop of the loop.

On the other hand, there is no built-in way to universally repeat multiple containers other than a normal loop. This is not surprising because there are many ways to repeat it. Think about it: you can go through one container with one step, one container with another step; or through one container until it reaches the end, then start inserting while you go to the end of the other container; or one step of the first container each time you completely go through another container, and then start over; or any other pattern; or more than two containers at a time; etc.

However, if you want to create your own for_each style function that iterates through two containers only to the shortest one, you can do something like this:

 template <typename Container1, typename Container2> void custom_for_each( Container1 &c1, Container2 &c2, std::function<void(Container1::iterator &it1, Container2::iterator &it2)> f) { Container1::iterator begin1 = c1.begin(); Container2::iterator begin2 = c2.begin(); Container1::iterator end1 = c1.end(); Container2::iterator end2 = c2.end(); Container1::iterator i1; Container1::iterator i2; for (i1 = begin1, i2 = begin2; (i1 != end1) && (i2 != end2); ++it1, ++i2) { f(i1, i2); } } 

Obviously, you can make any iteration strategy that you need in a similar way.

Of course, you can argue that just making the inner for loop is easier than writing a user-defined function like this ... and you will be right if you intend to do this only once or twice. But it's nice that it is very reusable. =)

+8
Sep 23 '12 at 14:40
source share

If you need to iterate only with two containers at a time, there is an extended version of the standard for_each algorithm in the extended range library, for example:

 #include <vector> #include <boost/assign/list_of.hpp> #include <boost/bind.hpp> #include <boost/range/algorithm_ext/for_each.hpp> void foo(int a, int& b) { b = a + 1; } int main() { std::vector<int> contA = boost::assign::list_of(4)(3)(5)(2); std::vector<int> contB(contA.size(), 0); boost::for_each(contA, contB, boost::bind(&foo, _1, _2)); // contB will be now 5,4,6,3 //... return 0; } 

When you need to handle more than 2 containers in one algorithm, you need to play with zip.

+5
Apr 29 '14 at 19:01
source share

another solution could capture the iterator link of another container in lambda and using the post increment operator. for example, a simple copy:

 vector<double> a{1, 2, 3}; vector<double> b(3); auto ita = a.begin(); for_each(b.begin(), b.end(), [&ita](auto &itb) { itb = *ita++; }) 

inside lambda you can do everything with ita and then increase it. This easily applies to a container with multiple containers.

+2
Jun 09 '16 at 20:15
source share

The range library provides this and other very useful features. The following example uses Boost.Range . Eric Niebler rangev3 should be a good alternative.

 #include <boost/range/combine.hpp> #include <iostream> #include <vector> #include <list> int main(int, const char*[]) { std::vector<int> const v{0,1,2,3,4}; std::list<char> const l{'a', 'b', 'c', 'd', 'e'}; for(auto const& i: boost::combine(v, l)) { int ti; char tc; boost::tie(ti,tc) = i; std::cout << '(' << ti << ',' << tc << ')' << '\n'; } return 0; } 

C ++ 17 will do it even better with structured bindings:

 int main(int, const char*[]) { std::vector<int> const v{0,1,2,3,4}; std::list<char> const l{'a', 'b', 'c', 'd', 'e'}; for(auto const& [ti, tc]: boost::combine(v, l)) { std::cout << '(' << ti << ',' << tc << ')' << '\n'; } return 0; } 
+1
Jul 25 '16 at 14:12
source share

Here is one option

 template<class ... Iterator> void increment_dummy(Iterator ... i) {} template<class Function,class ... Iterator> void for_each_combined(size_t N,Function&& fun,Iterator... iter) { while(N!=0) { fun(*iter...); increment_dummy(++iter...); --N; } } 

Usage example

 void arrays_mix(size_t N,const float* x,const float* y,float* z) { for_each_combined(N,[](float x,float y,float& z){z=x+y;},x,y,z); } 
0
Apr 18 '17 at 7:54 on
source share

I'm a little late too; but you can use this (C-style variational function):

 template<typename T> void foreach(std::function<void(T)> callback, int count...) { va_list args; va_start(args, count); for (int i = 0; i < count; i++) { std::vector<T> v = va_arg(args, std::vector<T>); std::for_each(v.begin(), v.end(), callback); } va_end(args); } foreach<int>([](const int &i) { // do something here }, 6, vecA, vecB, vecC, vecD, vecE, vecF); 

or this (using the function parameter package):

 template<typename Func, typename T> void foreach(Func callback, std::vector<T> &v) { std::for_each(v.begin(), v.end(), callback); } template<typename Func, typename T, typename... Args> void foreach(Func callback, std::vector<T> &v, Args... args) { std::for_each(v.begin(), v.end(), callback); return foreach(callback, args...); } foreach([](const int &i){ // do something here }, vecA, vecB, vecC, vecD, vecE, vecF); 

or this (using parenthesized list of initializers):

 template<typename Func, typename T> void foreach(Func callback, std::initializer_list<std::vector<T>> list) { for (auto &vec : list) { std::for_each(vec.begin(), vec.end(), callback); } } foreach([](const int &i){ // do something here }, {vecA, vecB, vecC, vecD, vecE, vecF}); 

or you can join vectors, like here: What is the best way to concatenate two vectors? and then iterate over the big vector.

0
Jul 28 '17 at 11:43
source share



All Articles