1

I am trying to combine two std::map containers (say std::map<int, int> foo, bar) into a third std::map (say std::map<int, int> foobar).

I know that I can achieve this using iterators as below:

    std::map<int, int>::iterator itr1 = foo.begin();
    std::map<int, int>::iterator itr2 = bar.begin();

    for (; itr1 != foo.end() && itr2 != bar.end(); ++itr1, ++itr2) 
    {
      foobar[itr1->first] += itr1->second;
      foobar[itr2->first] += itr2->second;
    }

But how can I use Range-based for loop (and maybe, in conjunction with Structured binding declaration) to achieve the same?

Or is there any better way to combine these two associative containers?

EDIT: The expected answer to this question should take two containers (std::map here) and combine them into a single union/united map with the keys being from both the respective associative containers and the values of duplicate keys being added.

Example: If given std::map containers foo and bar are:

  std::map<int, int> foo = {{1, 10}, {2, 20}, {3, 30}};
  std::map<int, int> bar = {{3, 50}, {4, 60}};

Then `foobar' should be:

  std::map<int, int> foobar = {{1, 10}, {2, 20}, {3, 80}, {4, 60}};

This all done in a single Range-based for loop.

Nitin Singla
  • 106
  • 2
  • 9
  • Relevant question: [What's the best way to iterate over two or more containers simultaneously](https://stackoverflow.com/q/12552277/580083) – Daniel Langr Jul 13 '20 at 07:51
  • just search for _C++ zip ranges_; this comes up all the time – underscore_d Jul 13 '20 at 09:15
  • seems like a dupe of [What's the best way to iterate over two or more containers simultaneously](https://stackoverflow.com/questions/12552277/whats-the-best-way-to-iterate-over-two-or-more-containers-simultaneously) - which covers both range-`for` and structured bindings. – underscore_d Jul 13 '20 at 09:15
  • @DanielLangr - Thanks! But the links in the top most answers there are broken. – Nitin Singla Jul 13 '20 at 11:43
  • 1
    Note that your loop will iterate off the end of the shorter container if the two containers have different sizes. – Marshall Clow Jul 13 '20 at 13:21

2 Answers2

6

is there any better way to combine these two associative containers?

The easiest way is probably to iterate over them separately:

for(auto[k, v] : foo) foobar[k] += v;
for(auto[k, v] : bar) foobar[k] += v;

If you want to discard any data already in foobar:

foobar = foo;
for(auto[k, v] : bar) foobar[k] += v;
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
-1

There's a standard library algorithm for doing just this: std::set_union, in <algorithm>. Now, granted, it's ugly because it users iterator pairs, but you can probably improve that using ranges.

Anyway:

std::set_union(
    foo.begin(), foo.end(), 
    bar.begin(), bar.end(),
    std::inserter(foobar, foobar.begin())
);

should do the trick.


PS - This may be a bit slow - as std::map itself is really slow. I assume you don't care about performance with this map (or it's really smaller), otherwise you shouldn't be using it.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 1
    Your code produces : foobar = {{1, 10}, {2, 20}, {3, 30}, {4, 60}}; Expected foobar = {{1, 10}, {2, 20}, {3, 80}, {4, 60}}; – Nitin Singla Jul 25 '20 at 14:30