如何从std :: map中检索所有键(或值)并将它们放入向量中?

344
这是我提出的一种可能的方法:
struct RetrieveKey
{
    template <typename T>
    typename T::first_type operator()(T keyValuePair) const
    {
        return keyValuePair.first;
    }
};

map<int, int> m;
vector<int> keys;

// Retrieve all keys
transform(m.begin(), m.end(), back_inserter(keys), RetrieveKey());

// Dump all keys
copy(keys.begin(), keys.end(), ostream_iterator<int>(cout, "\n"));

当然,我们也可以通过定义另一个函数对象RetrieveValues来从映射中检索所有值。

还有其他简单的方法实现吗?(我一直在想为什么std::map没有包含一个成员函数来做到这一点。)


14
你的解决方案是最好的。 - linello
5
我会尽力为您翻译这段话:我唯一想要补充的是 keys.reserve(m.size());。我的翻译如下:我只会在这里添加 keys.reserve(m.size()); - Galik
24个回答

231
虽然你的解决方案应该可以工作,但根据你的同行程序员的技能水平,它可能很难阅读。此外,它将功能移出了调用点,这可能会增加维护的难度。
我不确定你的目标是将键放入一个向量还是将它们打印到cout,所以我两者都做了。你可以尝试像这样的方法:
std::map<int, int> m;
std::vector<int> key, value;
for(std::map<int,int>::iterator it = m.begin(); it != m.end(); ++it) {
  key.push_back(it->first);
  value.push_back(it->second);
  std::cout << "Key: " << it->first << std::endl;
  std::cout << "Value: " << it->second << std::endl;
}

或者更简单一点,如果你正在使用Boost库:
map<int,int> m;
pair<int,int> me; // what a map<int, int> is made of
vector<int> v;
BOOST_FOREACH(me, m) {
  v.push_back(me.first);
  cout << me.first << "\n";
}

个人而言,我喜欢BOOST_FOREACH版本,因为它打字更少,并且非常明确地说明了它正在做什么。

4
你真的使用过 BOOST_FOREACH 吗?你在这里提出的代码完全是错误的。 - Manuel
2
@Jamie - 那是另一种方法,但是boost文档显示在BOOST_FOREACH之前指定变量及其类型(如果类型包含逗号)。他们还展示了typedef的用法。所以,我很困惑,我的代码有什么问题? - Jere.Jones
1
@手册 - 哦,一看到你的第一句话我就拍了拍自己的脑袋。谢谢!我已经更正了它,以免误导未来的访问者。 - Jere.Jones
19
有趣,预先调整向量大小以防止重新分配内存不是很合理吗? - Alan
5
别忘了执行v.reserve(m.size()),以避免在传输期间向量重新调整大小。 - Brian White
显示剩余8条评论

202
//c++0x too
std::map<int,int> mapints;
std::vector<int> vints;
for(auto const& imap: mapints)
    vints.push_back(imap.first);

7
不错。忘记 it = ...begin(); it != ...end 吧。当然,最好的方法是使用 std::map 并返回一个 keys() 方法来获取那个向量... - masterxilo
2
@BenHymers:在我看来,这个答案是在“2012年3月13日22:33回答”的,这已经是C++11成为C++的几个月之后了。 - Sebastian Mach
9
用for(auto &imap)更精确,因为不需要复制操作。 - ABCD
2
@StudentT,更好的做法是 for(auto const & imap : mapints) - cp.engr
2
我更喜欢使用 for (auto&& imap : mapints)。请参考 http://edmundv.home.xs4all.nl/blog/2014/01/28/use-auto-and-and-for-range-based-for-loops/。 - Rotsiser Mho
显示剩余5条评论

75

使用C++20的另一种方式

标准库中提供了ranges视图,该视图可检索成对/元组类型中的第一个元素:

#include <ranges>

auto kv = std::views::keys(m);
std::vector<int> keys{ kv.begin(), kv.end() };

值得一提的两个相关视图:

  1. values - 获取映射中的值(对/元组类型的第二个元素)
  2. elements - 获取元组类型中的第n个元素

5
这是现在最佳解决方案。 - Jay
3
这是一个非常容易实现的。C++已经走了很长的路! - Flynn O'Connell

68

有一个 boost range adaptor 用于此目的:

#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
vector<int> keys;
boost::copy(m | boost::adaptors::map_keys, std::back_inserter(keys));

还有一个类似的map_values范围适配器,可以提取值。


1
很不幸,boost::adaptors在Boost 1.43之前是不可用的。目前Debian(Squeeze)的稳定版本只提供了Boost 1.42。 - Mickaël Le Baillif
2
真遗憾。Boost 1.42于2010年2月发布,比Squeeze早了2.5年。 - Alastair
此时,Squeeze更新和/或后备存储库是否应该提供Boost 1.44? - Luis Machuca
这个头文件是在哪个 Boost 库中定义的? - James Wierzba
1
请查看链接文档,它在 boost/range/adaptor/map.hpp 中定义。 - Alastair

65

C++0x给我们提供了更进一步的,优秀的解决方案:

std::vector<int> keys;

std::transform(
    m_Inputs.begin(),
    m_Inputs.end(),
    std::back_inserter(keys),
    [](const std::map<int,int>::value_type &pair){return pair.first;});

31
在我看来,它没有任何优点。std::vector<int> keys; keys.reserve(m_Inputs.size()); for ( auto keyValue : m_Inputs){ keys.push_back(keyValue.first); } 相比那种晦涩的 transform,更好。甚至在性能方面也是如此。这个更好。 - Jagannath
5
如果您想获得可比较的性能,您也可以在这里保留键的大小。如果您想避免for循环,请使用转换(transform)。 - DanDan
4
只想补充一点 - 可以使用 [](const auto& pair)。 - ivan.ukr
@ivan.ukr 你在用哪个编译器?这种语法不被允许:*'const auto &'*:参数不能有包含'auto'的类型。 - Gobe
4
@ivan.ukr 在 C++14 中,lambda 表达式中的 auto 参数。 - roalz
显示剩余2条评论

38

基于 @rusty-parks 的解决方案,但是使用 c++17:

std::map<int, int> items;
std::vector<int> itemKeys;

for (const auto& [key, _] : items) {
    itemKeys.push_back(key);
}

我认为在结构化绑定中不能以这种方式使用std::ignore。我得到了一个编译错误。只需使用常规变量,例如ignored即可,它不会被使用,这应该足够了。 - j b
2
@j-b 谢谢。确实,std::ignore 旨在与 std::tie 一起使用,但不适用于结构化绑定。我已更新我的代码。 - Madiyar

23

@DanDan使用C++11的回答如下:

using namespace std;
vector<int> keys;

transform(begin(map_in), end(map_in), back_inserter(keys), 
            [](decltype(map_in)::value_type const& pair) {
    return pair.first;
}); 

正如@ivan.ukr所指出的,使用C++14,我们可以用auto替换decltype(map_in)::value_type


9
为了提高效率,你可以添加 keys.reserve(map_in.size()); - Galik
1
我发现使用transform方法需要的代码比for循环还要多。 - user1633272
const可以放在类型后面!我差点忘记了。 - Zhang
@user1633272> 是的,但这不是衡量它是否好的方法。abcauthorbookcustomer需要更少的代码,但是没有经验丰富的开发人员会告诉你要优先选择它们。生产代码不是代码高尔夫;) - spectras
我永远不会把变量命名为 pair,特别是在使用 using namespace std 后(在这种情况下,我会使用名称 entry)。 - Antonio

15

你的解决方案不错,但可以使用迭代器来完成:

std::map<int, int> m;
m.insert(std::pair<int, int>(3, 4));
m.insert(std::pair<int, int>(5, 6));
for(std::map<int, int>::const_iterator it = m.begin(); it != m.end(); it++)
{
    int key = it->first;
    int value = it->second;
    //Do something
}

12

SGI STL有一个扩展叫作select1st。很遗憾它没有成为标准STL的一部分!


8

以下是与C++11相关的内容:

std::map<uint32_t, uint32_t> items;
std::vector<uint32_t> itemKeys;
for (auto & kvp : items)
{
    itemKeys.emplace_back(kvp.first);
    std::cout << kvp.first << std::endl;
}

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