91

How can I merge two STL maps into one? They both have the same key and value types (map<string, string>). If there is an overlap of the keys, I would like to give preference to one of the maps.

honk
  • 9,137
  • 11
  • 75
  • 83
JonF
  • 2,336
  • 6
  • 38
  • 57

5 Answers5

152

Assuming you want to preserve the elements in mapA, and merge elements in mapB for which there is no key in mapA:

mapA.insert(mapB.begin(), mapB.end())

will do what you want, I think.

(EDIT: If you are using C++17 or newer, consider this answer: https://stackoverflow.com/a/56594603/118150)

Working example:

#include <iostream>
#include <map>

void printIt(std::map<int,int> m) {
    for(std::map<int,int>::iterator it=m.begin();it!=m.end();++it)
        std::cout << it->first<<":"<<it->second<<" ";
    std::cout << "\n";
}

int main() {
    std::map<int,int> foo,bar;
    foo[1] = 11; foo[2] = 12; foo[3] = 13;
    bar[2] = 20; bar[3] = 30; bar[4] = 40;
    printIt(foo);
    printIt(bar);
    foo.insert(bar.begin(),bar.end());
    printIt(foo);
    return 0;
}

output:

:!./insert
1:11 2:12 3:13
2:20 3:30 4:40
1:11 2:12 3:13 4:40
jkerian
  • 16,497
  • 3
  • 46
  • 59
  • I can't see how that doesn't override a duplicate in mapA if the keys match. If I just say that mapB was my "preferred" map I could use this I think. That way if it's a duplicate then it the key in mapB would be the one that ultimately ends on in the new map (which is now mapA). Does that sound correct or am I misunderstanding what insert does when there is a duplicatE? – JonF Sep 03 '10 at 22:43
  • 15
    Insert will not overwrite existing elements, when there is a clash in the keys, the already existing element takes precedence. – David Rodríguez - dribeas Sep 03 '10 at 23:20
  • oh i see. unfortunetly it doesn't build though. It generates a huge error message – JonF Sep 07 '10 at 16:54
  • Sample code added, the basic concept is sound. What sort of error? (Ideally on a simplified code path) – jkerian Sep 07 '10 at 22:04
  • 2
    What is the complexity of this? Is it n log(n), where n is the number of elements in the source map. Or can the complexity be lower (merging two red-black trees)? – user877329 Jul 02 '16 at 13:19
  • 2
    The standard says complexity is n log(n) (source:cppreference) – galinette Jan 10 '17 at 12:34
  • 2
    @galinette Not quite, it is O(n log(n+m)) where n is the size of the source range (in this case, indeed the size of the source map), and m is the size of the destination map. (It could be implemented as something like O(n (1 + log(m/(1+n))) + log(m)) in the special case that the source range is sorted by the destination's `value_comp()`, but the standard does not mandate that.) – Arne Vogel Nov 08 '18 at 12:15
38

If you want to copy entries from one map to another, you can use std::map's insert:

targetMap.insert(sourceMap.begin(), sourceMap.end());

But note that insert does not update elements if their key is already in targetMap; those items will be left as-is. To overwrite elements, you will have to copy explicitly, e.g.:

for(auto& it : sourceMap)
{
    targetMap[it.first] = it.second;
}

If you don't mind losing the data in sourceMap, another way to achieve a copy-and-overwrite is to insert the target into the source and std::swap the results:

sourceMap.insert(targetMap.begin(), targetMap.end());
std::swap(sourceMap, targetMap);

After swapping, sourceMap will contain targetMap's old data, and targetMap will be a merge of the two maps, with preference for sourceMap's entries.

kiwibonga
  • 587
  • 4
  • 13
23

C++17

As mentioned in John Perry's answer, since C++17 std::map provides a merge() member function. The merge() function produces the same result for the target map as jkerian's solution based on using insert(), as you can see from the following example, which I borrowed from jkerian. I just updated the code with some C++11 and C++17 features (such as using type alias, range-based for loop with structured binding, and list initialization):

using mymap = std::map<int, int>;

void printIt(const mymap& m) {
    for (auto const &[k, v] : m)
        std::cout << k << ":" << v << " ";
    std::cout << std::endl;
}

int main() {
    mymap foo{ {1, 11}, {2, 12}, {3, 13} };
    mymap bar{ {2, 20}, {3, 30}, {4, 40} };
    printIt(foo);
    printIt(bar);
    foo.merge(bar);
    printIt(foo);
    return 0;
}

Output:

1:11 2:12 3:13
2:20 3:30 4:40
1:11 2:12 3:13 4:40

As you can see, merge() also gives priority to the target map foo when keys overlap. If you want to have it the other way round, then you have to call bar.merge(foo);.

However, there is a difference between using insert() and merge() regarding what happens to the source map. The insert() functions adds new entries to the target map, while merge() moves entries over from the source map. This means for the example above, that insert() does not alter bar, but merge() removes 4:40 from bar, so that only 2:20 and 3:30 remain in bar.

Note: I reused the example from jkerian which uses map<int, int> for the sake of brevity, but merge() also works for your map<string, string>.

Code on Coliru

honk
  • 9,137
  • 11
  • 75
  • 83
18

Notice that, since C++17, there is a merge() method for maps.

VLL
  • 9,634
  • 1
  • 29
  • 54
John Perry
  • 2,497
  • 2
  • 19
  • 28
3

According to ISO/IEC 14882:2003, section 23.1.2, Table 69, expression a.insert(i,j):

pre: i,j are not iterators into a. inserts each element from the range [i, j) if and only if there is no element with key equivalent to the key of that element in containers with unique keys;

Since that std::map must follow this restriction, if you'd like to give preference to "values" from one map over another you should insert into it. For example,

std::map<int, int> goodKeys;
std::map<int, int> betterKeys;

betterKeys.insert(goodKeys.begin(), goodKeys.end());

So if there are any equivalent keys in goodKeys and betterKeys, "values" of the betterKeys will be preserved.