303

这是我出来的可能方式之一:

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 不包含一个成员函数供我们这样做。)

4

22 回答 22

205

虽然您的解决方案应该有效,但根据您的程序员同事的技能水平,可能难以阅读。此外,它将功能从呼叫站点移开。这会使维护变得更加困难。

我不确定您的目标是将密钥放入向量中还是将它们打印到 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 版本,因为它的输入更少,而且它的功能非常明确。

于 2008-09-21T04:38:34.450 回答
186
//c++0x too
std::map<int,int> mapints;
std::vector<int> vints;
for(auto const& imap: mapints)
    vints.push_back(imap.first);
于 2012-03-13T22:33:32.230 回答
67

为此目的有一个升压范围适配器:

#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 范围适配器用于提取值。

于 2012-03-05T19:29:46.923 回答
54

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;});
于 2010-05-08T13:07:44.007 回答
23

基于@rusty-parks 解决方案,但在 c++17 中:

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

for (const auto& [key, _] : items) {
    itemKeys.push_back(key);
}
于 2019-05-03T21:08:40.443 回答
22

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

并使用 C++14(如 @ivan.ukr 所述),我们可以decltype(map_in)::value_typeauto.

于 2016-09-16T12:47:48.270 回答
15

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

范围库有一个键视图,它检索类似对/元组类型的第一个元素:

#include <ranges>

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

两个相关的观点值得一提:

  1. values - 获取地图中的值(一对/元组类型中的第二个元素)
  2. 元素 - 以类似元组的类型获取第 n 个元素
于 2021-06-23T06:25:44.133 回答
14

您的解决方案很好,但您可以使用迭代器来执行此操作:

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
}
于 2008-09-21T03:57:37.183 回答
12

SGI STL 有一个名为select1st. 太糟糕了,它不在标准 STL 中!

于 2008-09-21T03:31:46.543 回答
9

我认为上面介绍的 BOOST_FOREACH 很好而且很干净,但是,还有另一个使用 BOOST 的选项。

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

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

using namespace boost::lambda;

transform(      m.begin(), 
                m.end(), 
                back_inserter(keys), 
                bind( &std::map<int,int>::value_type::first, _1 ) 
          );

copy( keys.begin(), keys.end(), std::ostream_iterator<int>(std::cout, "\n") );

就个人而言,我认为这种方法在这种情况下不如 BOOST_FOREACH 方法那么干净,但在其他情况下 boost::lambda 可能真的很干净。

于 2008-09-21T06:24:03.620 回答
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;
}
于 2016-03-10T01:06:46.533 回答
8

这是一个使用 C++11 魔法的不错的函数模板,适用于 std::map 和 std::unordered_map:

template<template <typename...> class MAP, class KEY, class VALUE>
std::vector<KEY>
keys(const MAP<KEY, VALUE>& map)
{
    std::vector<KEY> result;
    result.reserve(map.size());
    for(const auto& it : map){
        result.emplace_back(it.first);
    }
    return result;
}

在这里查看:http: //ideone.com/lYBzpL

于 2016-08-10T23:34:39.473 回答
7

此外,如果您有 Boost,请使用 transform_iterator 以避免制作密钥的临时副本。

于 2008-09-21T04:05:36.230 回答
6

您可以使用通用的 boost::transform_iterator。transform_iterator 允许您转换迭代的值,例如在我们的例子中,当您只想处理键而不是值时。请参阅http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/transform_iterator.html#example

于 2008-09-21T04:14:42.167 回答
6

使用C++17结构化绑定(“解构”)声明语法

你可以这样做,这更容易理解。

// To get the keys
std::map<int, double> map;
std::vector<int> keys;
keys.reserve(map.size());
for(const auto& [key, value] : map) {
    keys.push_back(key);
}
// To get the values
std::map<int, double> map;
std::vector<double> values;
values.reserve(map.size());
for(const auto& [key, value] : map) {
    values.push_back(value);
}
于 2021-01-27T12:12:47.987 回答
4

最好的非 sgi、非升压 STL 解决方案是像这样扩展 map::iterator:

template<class map_type>
class key_iterator : public map_type::iterator
{
public:
    typedef typename map_type::iterator map_iterator;
    typedef typename map_iterator::value_type::first_type key_type;

    key_iterator(const map_iterator& other) : map_type::iterator(other) {} ;

    key_type& operator *()
    {
        return map_type::iterator::operator*().first;
    }
};

// helpers to create iterators easier:
template<class map_type>
key_iterator<map_type> key_begin(map_type& m)
{
    return key_iterator<map_type>(m.begin());
}
template<class map_type>
key_iterator<map_type> key_end(map_type& m)
{
    return key_iterator<map_type>(m.end());
}

然后像这样使用它们:

        map<string,int> test;
        test["one"] = 1;
        test["two"] = 2;

        vector<string> keys;

//      // method one
//      key_iterator<map<string,int> > kb(test.begin());
//      key_iterator<map<string,int> > ke(test.end());
//      keys.insert(keys.begin(), kb, ke);

//      // method two
//      keys.insert(keys.begin(),
//           key_iterator<map<string,int> >(test.begin()),
//           key_iterator<map<string,int> >(test.end()));

        // method three (with helpers)
        keys.insert(keys.begin(), key_begin(test), key_end(test));

        string one = keys[0];
于 2010-03-05T19:15:20.503 回答
3

我发现以下三行代码是最简单的方法:

// save keys in vector

vector<string> keys;
for (auto & it : m) {
    keys.push_back(it.first);
}

这是此答案的第一种方式的缩短版本。

于 2020-07-30T22:50:10.897 回答
1

以下仿函数检索映射的键集:

#include <vector>
#include <iterator>
#include <algorithm>

template <class _Map>
std::vector<typename _Map::key_type> keyset(const _Map& map)
{
    std::vector<typename _Map::key_type> result;
    result.reserve(map.size());
    std::transform(map.cbegin(), map.cend(), std::back_inserter(result), [](typename _Map::const_reference kvpair) {
        return kvpair.first;
    });
    return result;
}

奖励:以下仿函数检索映射的值集:

#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>

template <class _Map>
std::vector<typename _Map::mapped_type> valueset(const _Map& map)
{
    std::vector<typename _Map::mapped_type> result;
    result.reserve(map.size());
    std::transform(map.cbegin(), map.cend(), std::back_inserter(result), [](typename _Map::const_reference kvpair) {
        return kvpair.second;
    });
    return result;
}

template <class _Map>
std::vector<std::reference_wrapper<typename _Map::mapped_type>> valueset(_Map& map)
{
    std::vector<std::reference_wrapper<typename _Map::mapped_type>> result;
    result.reserve(map.size());
    std::transform(map.begin(), map.end(), std::back_inserter(result), [](typename _Map::reference kvpair) {
        return std::ref(kvpair.second);
    });
    return result;
}

用法

int main()
{
    std::map<int, double> map{
        {1, 9.0},
        {2, 9.9},
        {3, 9.99},
        {4, 9.999},
    };
    auto ks = keyset(map);
    auto vs = valueset(map);
    for (auto& k : ks) std::cout << k << '\n';
    std::cout << "------------------\n";
    for (auto& v : vs) std::cout << v << '\n';
    for (auto& v : vs) v += 100.0;
    std::cout << "------------------\n";
    for (auto& v : vs) std::cout << v << '\n';
    std::cout << "------------------\n";
    for (auto& [k, v] : map) std::cout << v << '\n';

    return 0;
}

预期输出:

1
2
3
4
------------------
9
9.9
9.99
9.999
------------------
109
109.9
109.99
109.999
------------------
109
109.9
109.99
109.999
于 2020-12-20T10:08:15.687 回答
0

以原子图为例

#include <iostream>
#include <map>
#include <vector> 
#include <atomic>

using namespace std;

typedef std::atomic<std::uint32_t> atomic_uint32_t;
typedef std::map<int, atomic_uint32_t> atomic_map_t;

int main()
{
    atomic_map_t m;

    m[4] = 456;
    m[2] = 45678;

    vector<int> v;
    for(map<int,atomic_uint32_t>::iterator it = m.begin(); it != m.end(); ++it) {
      v.push_back(it->second);
      cout << it->first << " "<<it->second<<"\n";
    }

    return 0;
}
于 2020-03-24T19:39:08.490 回答
0

您可以使用 fplus中的 get_map_keys() :

#include<fplus/maps.hpp>
// ...

int main() {
    map<string, int32_t> myMap{{"a", 1}, {"b", 2}};
    vector<string> keys = fplus::get_map_keys(myMap);
    // ...
    return 0;
}
于 2021-06-07T10:10:47.990 回答
-2

与此处的一个示例略有相似,从std::map使用角度进行了简化。

template<class KEY, class VALUE>
std::vector<KEY> getKeys(const std::map<KEY, VALUE>& map)
{
    std::vector<KEY> keys(map.size());
    for (const auto& it : map)
        keys.push_back(it.first);
    return keys;
}

像这样使用:

auto keys = getKeys(yourMap);
于 2019-04-14T14:12:52.080 回答
-4

(我一直想知道为什么 std::map 不包含一个成员函数供我们这样做。)

因为它不能比你做的更好。如果一个方法的实现并不优于自由函数的实现,那么通常你不应该编写一个方法;你应该写一个自由函数。

也不清楚为什么它还是有用的。

于 2008-09-21T11:13:15.170 回答