如何合并 2 个 std::maps,并在第三个映射中输出结果
Posted
技术标签:
【中文标题】如何合并 2 个 std::maps,并在第三个映射中输出结果【英文标题】:How to merge 2 std::maps, and output the result in a 3rd map 【发布时间】:2015-12-17 17:13:41 【问题描述】:编辑原始问题,因为我想问关于std::map
的问题,而不是std::vector
。我的错。对不起。
我的数据实际上在 2 个std::map
s 中。我想将两张地图合并为第三张地图。
我的第一个和第二个映射包含具有相同键的条目。因此,我也想将这些键下的数据合并到生成的第三张地图中。
所以如果我使用std::merge
,我会丢失第一个或第二个地图常用条目中的数据吗?正如我所提到的,两个地图中都有共同的数据(值)?
【问题讨论】:
std::merge
?
各个向量中是否有重复项?
在阅读 cmets 到另一个答案后,我注意到您并没有询问您的真正问题,而是询问您尝试解决它的方法。这通常称为XY-problem。请编辑问题并包含原始问题。合并两个std::map
s 可以更轻松、更快捷地完成。
Revolver_Ocelot 建议编辑的原始问题
@XMarshall:请不要以使答案无效的方式编辑问题。这些人回答了你写的问题。如果这些都不能解决您的问题,请创建一个新问题而不是编辑以使其无效。
【参考方案1】:
顺序容器的信息
如果对vector
s(或其他顺序容器,如list
或deque
)进行了排序,则可以使用std::set_union
。有一个std::merge
,以备不时之需
取自链接页面的代码:
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
int main()
std::vector<int> v1 = 1, 2, 3, 4, 5;
std::vector<int> v2 = 3, 4, 5, 6, 7;
std::vector<int> dest1;
std::set_union(v1.begin(), v1.end(),
v2.begin(), v2.end(),
std::back_inserter(dest1));
for (const auto &i : dest1)
std::cout << i << ' ';
std::cout << '\n';
输出:1 2 3 4 5 6 7
关联容器信息
您可以使用insert
方法向map
(或set
及其无序对应项)添加唯一键。原始映射中已经存在的任何键都不会被替换(在multiset
和multimap
中将添加重复键,如果应该避免这种情况,请使用前面讨论过的set_union
)。
地图示例(忽略具有重复键的条目):
#include <map>
#include <iostream>
int main()
std::map<int, int> v1 = 1, 1, 2, 1, 3, 1, 4, 1, 5, 1;
std::map<int, int> v2 = 3, 2, 4, 2, 5, 2, 6, 2, 7, 2;
std::map<int, int> dest1 = v1;
dest1.insert(v2.begin(), v2.end());
for (const auto &i : dest1)
std::cout << i.first << ':' << i.second << ' ';
std::cout << '\n';
输出(第一个数字是键,第二个——它来自哪个映射):1:1 2:1 3:1 4:1 5:1 6:2 7:2
如果您切换地图(将 dest1
设置为开头的 v2
的副本和 v1
的插入值),则输出将为:1:1 2:1 3:2 4 :2 5:2 6:2 7:2
多地图示例(保留两个地图的条目):
#include <map>
#include <iostream>
int main()
std::map<int, int> v1 = 1, 1, 2, 1, 3, 1, 4, 1, 5, 1;
std::map<int, int> v2 = 3, 2, 4, 2, 5, 2, 6, 2, 7, 2;
std::multimap<int, int> dest1 v1.begin(), v1.end();
dest1.insert(v2.begin(), v2.end());
for (const auto &i : dest1)
std::cout << i.first << ':' << i.second << ' ';
std::cout << '\n';
输出:1:1 2:1 3:1 3:2 4:1 4:2 5:1 5:2 6:2 7:2
请注意,您不能像使用地图一样使用multimap
。例如,operator[]
不可用。您将需要使用insert
来添加条目,并使用lower_bound
或equal_range
成员函数来提取条目。
【讨论】:
谢谢。就我而言,它有点复杂。我已经有 2 张包含数据的地图。 map中的key是一个字符串,对应的数据是一个结构体,结构体又包含一些其他的数据。现在,使用上述方法,我在地图中的迭代和应用业务逻辑是否有效?两张地图中包含的数据量也非常大。但是,在处理生成的第三张地图和最终地图时,我会知道前两张地图的大小。 @XMarshall ,如果您的意图是让第三张地图充满前两张地图的唯一数据副本而不更改原件,那么是的,这将是最有效的解决方案。如果您不关心原始内容,您可以简单地将第二个插入第一个,如果您的数据启用了移动,则遍历您的第二个地图,将内容移动到第一个。【参考方案2】:这就是std::merge
的用途:
std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(v3));
【讨论】:
std::merge
不会消除重复。
@AnonMail 请准确阅读问题:“没有重复”
我读过。只是不是 100% 清楚“没有重复”是指什么。
谢谢。我在问这个问题时犯了一个大错误。我的数据实际上在 2 个 std::maps 中。对不起这个错误。因此,如果我使用 std::merge,我会丢失第一张或第二张地图中的数据吗?两个地图都有共同的数据(键)。
使用std::merge
,您不会失去任何东西。使用std::set_union
,您将丢失重复项。目前尚不清楚保留哪个项目,但如果它们确实是重复的,那应该没关系。 std::set_symmetric-difference
不会做你想做的事。【参考方案3】:
我相信您想要的是既能合并地图又能合并地图的内容的东西。
template<class Map, class Merge>
Map careful_merge( Map lhs, Map const& rhs, Merge&& merge )
for (auto&& rhs_entry : rhs)
auto lhs_it = lhs.find(rhs_entry.first);
if (lhs_it == lhs.end())
lhs.insert( rhs_entry );
continue;
auto& lhs_value = lhs_it.second;
lhs_value = merge(std::move(lhs_value), rhs.second);
return lhs;
这是一个辅助函数,它接受两个关联容器,如果值在两个容器中,则调用 merge
。
假设您有一个std::map< std::string, std::vector<int> >
并且想要合并它们,以便结果附加映射到的向量。那么:
using my_map = std::map< std::string, std::vector<int> >;
my_map a, b; // fill these somehow.
my_map result = careful_merge( a, b,
[](std::vector<int> lhs, std::vector<int> const& rhs )
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return std::move(lhs);
);
我们完成了。
左侧或右侧唯一的条目永远不会合并,只是逐字复制到输出中。
假定有效的移动语义。
它适用于有序和无序的地图。它不适用于套装。我建议不要在多地图上使用它,即使它有效(我不确定)因为它不是为他们设计的。
【讨论】:
【参考方案4】:请注意,从 C++17 开始,现在有一个 std::map::merge(other_map)
方法。
见https://en.cppreference.com/w/cpp/container/map/merge
【讨论】:
以上是关于如何合并 2 个 std::maps,并在第三个映射中输出结果的主要内容,如果未能解决你的问题,请参考以下文章