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

时间:2008-09-21 03:23:58

标签: c++ dictionary stl stdmap

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

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

22 个答案:

答案 0 :(得分:153)

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

我不确定你的目标是将密钥放入矢量中还是将它们打印到cout所以我正在做两件事。你可以尝试这样的事情:

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

甚至更简单,如果你使用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版本,因为键入的内容较少,而且它的内容非常明确。

答案 1 :(得分:133)

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

答案 2 :(得分:56)

为此目的有一个boost range adaptor

vector<int> keys;
// Retrieve all keys
boost::copy(m | boost::adaptors::map_keys, std::back_inserter(keys));

有一个类似的map_values范围适配器用于提取值。

答案 3 :(得分:42)

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

答案 4 :(得分:11)

@ 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_type替换为auto

答案 5 :(得分:10)

SGI STL有一个名为select1st的扩展名。太糟糕了,它不是标准的STL!

答案 6 :(得分: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可能非常干净。

答案 7 :(得分:7)

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

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
}

答案 8 :(得分:6)

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

答案 9 :(得分:5)

您可以使用多功能的boost :: transform_iterator。 transform_iterator允许您转换迭代值,例如在我们只想处理键而不是值的情况下。见http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/transform_iterator.html#example

答案 10 :(得分:4)

c ++ 11的一点点:

ref

答案 11 :(得分:4)

这是一个很好的使用C ++ 11 magic的函数模板,适用于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

答案 12 :(得分:4)

最好的非sgi,非boost 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];

答案 13 :(得分:3)

使用 the structured binding (“destructuring”) declaration syntax 的 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);
}

答案 14 :(得分:1)

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

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

#include <ranges>

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

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

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

答案 15 :(得分:0)

与此处的示例之一类似,从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);

答案 16 :(得分:0)

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

std::map<uint32_t, uint32_t> items;
std::vector<uint32_t> itemKeys;
for (auto const& [key, std:ignore] : items) {
  itemKeys.emplace_back(key);
}

答案 17 :(得分: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;
}

答案 18 :(得分:0)

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

// save keys in vector

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

这是this answer第一种方式的简化版本。

答案 19 :(得分:0)

以下函子检索地图的键集:

#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

答案 20 :(得分:0)

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

John Doe Interests are
<ul>
  <li>Football</li>
  <li>Hockey</li>
</ul>
Good

答案 21 :(得分:-2)

  

(我总是想知道为什么std :: map不包含我们这样做的成员函数。)

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

目前还不清楚为什么它仍然有用。