如何合并两个STL映射?


74

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

Answers:


134

假设您要保留中的元素mapA,并合并mapB其中没有关键字的元素mapA

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

我想会做你想要的。

(编辑:如果您使用的是C ++ 17或更高版本,请考虑以下答案:https : //stackoverflow.com/a/56594603/118150

工作示例:

#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;
}

输出:

:!./insert
1:11 2:12 3:13
2:20 3:30 4:40
1:11 2:12 3:13 4:40

如果键匹配,我看不到怎么不会覆盖mapA中的重复项。如果我只是说mapB是我的“首选”地图,我想我可以使用它。这样,如果它是重复项,则mapB中的密钥将是最终在新映射(现在为mapA)中结束的密钥。这听起来是正确的,还是我误解了重复的情况下该插入什么内容?
JonF

13
插入不会覆盖现有元素,如果键中有冲突,则以现有元素为准。
DavidRodríguez-dribeas 2010年

哦,我懂了。不幸的是,它没有建立。它生成一个巨大的错误消息
JonF

2
该标准表示,复杂度为n log(n)(来源:cppreference)
galinette

2
@galinette不完全是O(n log(n + m)),其中n是源范围的大小(在这种情况下,实际上是源地图的大小),而m是目标地图的大小。(它可以被实现为在特殊情况下,像为O(n(1 +日志(M /(1 + N)))+日志(M)),其源极范围由目的地的排序value_comp(),但标准不强制要求)。
阿恩·沃格尔

35

如果要将条目从一个地图复制到另一个地图,可以使用std::mapinsert

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

但是请注意,insert如果元素的键已经在targetMap中,则不会更新元素;这些项目将保持原样。要覆盖元素,必须显式复制,例如:

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

如果您不介意丢失数据sourceMap,则实现复制和覆盖的另一种方法是insert将目标复制到源和std::swap结果中:

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

交换后,sourceMap将包含targetMap的旧数据,并且targetMap将是两个地图的合并,并且优先于sourceMap的条目。



10

C ++ 17

John Perry的回答中所述,由于C ++ 17 std::map提供了merge()成员函数。如下面的示例所示,该merge()函数对目标地图产生的结果与jkerian基于using的解决方案相同insert(),这是我从jkerian借来的。我刚刚使用一些C ++ 11和C ++ 17功能(例如using类型别名,具有结构化绑定的基于范围的for循环列表初始化)更新了代码:

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;
}

输出:

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

如您所见,当按键重叠时,merge()也会优先考虑目标地图foo。如果您想反过来使用它,则必须致电bar.merge(foo);

但是,使用insert()merge()考虑源地图发生了什么区别。该insert()功能将新条目添加到目标地图,同时merge()将条目从源地图移到上方。这意味着对于上面的示例,该示例insert()不会更改bar,但merge()会删除4:40从中bar,因此仅2:203:30保留在中bar

注意:map<int, int>为了方便起见,我重用了jkerian的示例,该示例merge()也适用于您map<string, string>

关于Coliru的代码


3

根据ISO / IEC 14882:2003第23.1.2节,表69,表达式a.insert(i,j):

上一条:i,j不是a的迭代器。仅当且仅当在具有唯一键的容器中没有键的元素等效于该元素的键时,才插入[i,j)范围内的每个元素;

由于该std :: map必须遵循此限制,因此,如果您要优先考虑一个映射中的“值”而不是另一个映射,则应插入其中。例如,

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

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

因此,如果goodKeys和betterKeys中有任何等效的键,则将保留betterKeys的“值”。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.