如何合并两个 STL 映射?

如何将两个 STL 映射合并为一个?它们都具有相同的键和值类型(map<string, string>)。如果键有重叠,我想优先选择其中一个映射。

78960 次浏览

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

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.

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.

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

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 merge()0, as you can see from the following example, which I borrowed from jkerian. I just updated the code with some merge()1 and C++17 features (such as merge()2, merge()3 with merge()4, and merge()5):

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 merge()0 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