如何将 std::unordered_multimap<uint, T> 转换为 std::vector<T>?

4
我将尝试将 std::unordered_multimap<uint, T> lookup 转换为 std::vector<T> v
到目前为止,我尝试了以下内容:
std::vector<T> v(lookup.begin(), lookup.end());

但显然这并不起作用,因为begin()end()的结果迭代器是pair<uint, T>类型,那么最快的正确方法是什么呢?

感谢您的帮助!


你能保证在索引中没有“空洞”吗?或者说uint值会被丢弃吗? - Jarod42
1
你可以遍历这个 map 并将 T 压入 vector 中。你也可以编写一个包装器,使其在解引用时返回 T 而不是 pair<uint, T> - nwp
我希望v包含在我的multimap中存储的所有T值。 - Luis Yanes
@Jarod42,我认为OP并不希望向量索引具有与multimap键相同的映射。 - eerorika
@user2079303:这是我的第二个问题,(我犯了一个错误,一次问了两个问题,所以只得到了一个答案 :( )。 - Jarod42
显示剩余2条评论
5个回答

6

从哈希映射中提取std::pair的值部分并将其放入向量中:

#include <iostream>
#include <unordered_map>
#include <vector>

int main() {
    using Map = std::unordered_multimap<int, int>;
    auto m = Map { {1, 1}, {1, 2}, {1, 3}, {2, 4}, {2, 5}, {3, 6} };

    std::vector<int> v;
    v.reserve(m.size());

    for (auto const& elem : m)
        v.push_back(elem.second);

    for (auto const& elem : v)
        std::cout << elem << "\n";
}

请注意,使用C++11,您可以使用范围for循环+ auto 来避免明确表达映射元素的类型。此外,您还可以使用初始化程序列表语法快速初始化映射。 注意:在实际示例中,请务必在向量上使用reserve()以避免过度内存分配。 在线示例

@Jarod42 我有时会过度使用STL算法。根据Sean Parent的说法,多达2个函数并不等同于原始循环;-) - TemplateRex
дљ†дЄЇдїАдєИи¶БзФ®for_eachеТМback_inserterдњЃжФєеОЯжЭ•зЪДз≠Фж°ИпЉЯ - Sambuca
@Sambuca 因为它过于复杂了。一般来说,我更喜欢 "无原始循环"(第39页)但是STL算法方法。但是像 f(g(x)), f(x); g(x) 或者 f(x) + g(x) 这样简单的东西不被认为是原始循环。范围for循环是完成这个简单任务最紧凑的方式。 - TemplateRex
谢谢,我在想是否有一种方法可以直接复制内存,而不必实际遍历整个内存。但是,我想这个解决方案就只能这样了... - Luis Yanes
@LuisYanes 哈希映射没有连续的内存布局,因此您需要遵循迭代器。 - TemplateRex

5

试试这个:

std::vector<T> v;
v.reserve(lookup.size()); // optimization (allocate enough memory for all elements)

std::transform(std::begin(lookup), std::end(lookup), std::back_inserter(v),
    [](const std::pair<uint, T>& p) { return p.second; });

我建议使用自C++11以来的std :: begin和std :: end自由函数编写它 :) - Germán Diago
@GermánDiago 好的,争论口味可能没有意义,但是一个算法+lambda+back_inserter+显式类型比范围for循环+自动更优雅在哪里? - TemplateRex
请注意,由于参数相关的查找,大多数std::限定是不必要的。 - Benno
@TemplateRex 不需要使用back inserter,那是一个错误。在我看来,命名算法比for循环更能明确意图。 - Germán Diago
总的来说是这样的,但对于简单的(其中简单表示小于等于2个函数调用的)循环/转换,range-for更简单、更清晰。请参见 Sean Parent 的建议(第39页,pdf中的第102页)。 - TemplateRex
@TemplateRex 嗯,我看到了。我的观点是两者都可读,但读取名称更加清晰。我并不是说它真的更短或更好。两者都可以,我更喜欢 transform :) - Germán Diago

1
使用循环来push_back向量中的项目。当然,这需要类型T是可复制构造的:
for ( auto it = map.begin(); it != map.end(); it++)
    v.push_back(it->second);

如果你想要更精细的解决方案,可以通过使用reserve在向量中预留足够的空间来进行优化。此外,如果你打算在之后丢弃地图,可以尝试避免复制项目而是将它们移入其中:

v.reserve(map.size());
for ( auto it = map.begin(); it!= map.end(); it++)
    v.emplace_back(std::move(it->second));

0

最近我一直在尝试使用C++11。这是我想出来的东西。

#include <iostream>
#include <iterator>
#include <vector>
#include <unordered_map>
#include <algorithm>

int main() {
    std::vector<int> v;
    typedef std::unordered_multimap<int, int> MapType;

    MapType m { { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 }, { 3, 1 } };
    std::for_each(begin(m), end(m), [&](MapType::value_type i){v.push_back(i.second); });
    std::copy(begin(v), end(v), std::ostream_iterator<int>(std::cout, " "));

    return 0;
}

在线运行我的代码。

诚然,使用STL算法来解决这个问题有点过度杀伐。


为什么要混合使用 for_eachfor range - Jarod42
就像我说的,我一直在尝试使用C++11的特性——没有特别的原因。OP的问题只是类似于我已经在我的测试项目中运行的东西。 - Sambuca
如果您使用C++11,请确保使用初始化列表。 - TemplateRex
同意@Jarod42的观点:range-for循环将完全消除迭代器+lambda的使用,它允许使用“auto”来表示映射的元素类型(对于在lambda中执行此操作,您需要C++1y)。 - TemplateRex
@TemplateRex 是的,没错。你的最终答案非常简洁 :) - Sambuca

0

我的方法是:[注意未经测试]

template <typename Map>
std::vector<typename Map::mapped_type> extract_values(Map const& m) {
    std::vector<typename Map::mapped_type> v;
    v.reserve(m.size());
    std::for_each(std::begin(m), std::end(m), [&](typename Map::const_reference p)
        { v.push_back(p.second); }
    );
    return v;
}

更简单的方法:
template <typename Map>
std::vector<typename Map::mapped_type> extract_values(Map const& m) {
    std::vector<typename Map::mapped_type> v;
    v.reserve(m.size());
    for(auto& p : m)
        v.push_back(p.second);
    return v;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接