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

这是我提出的一种可能的方法:

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

502008 次浏览

SGI STL有一个名为select1st的扩展。可惜不是标准的STL!

你的解决方案是好的,但你可以使用迭代器来做:

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
}

此外,如果您有Boost,请使用transform_iterator来避免生成键的临时副本。

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

虽然您的解决方案应该可以工作,但根据其他程序员的技能水平,它可能很难阅读。此外,它将功能从调用站点移走。这可能会使维护更加困难。

我不确定你的目标是将键输入到向量中还是将它们打印到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版本,因为它的输入更少,而且它非常明确地说明了它在做什么。

我认为上面提出的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在其他情况下可以非常干净。

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

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

目前也不清楚为什么它有用。

最好的非sgi、非boost STL解决方案是扩展map::迭代器,如下所示:

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];

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

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

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

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

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

这是一个使用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

@丹丹的答案,使用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所述),我们可以用auto替换decltype(map_in)::value_type

有点类似于这里的一个例子,从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);

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

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


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

使用原子映射示例

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

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

// save keys in vector


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

它是这个答案的第一种方法的缩短版本。

下面的函子检索映射的键集:

#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

c++ 17中的结构化绑定(" destructuring ")声明语法

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

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

你可以使用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;
}

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

ranges库有一个keys视图,用于检索类对/元组类型的第一个元素:

#include <ranges>


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

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

  1. Values—获取map中的值(类元组/对类型中的第二个元素)
  2. Elements—获取类元组类型中的第n个元素

在c++ 20中使用范围,你可以像这样使用std::ranges::copy

#include <ranges>
std::map<int,int> mapints;
std::vector<int> vints;


std::ranges::copy(mapints | std::views::keys, std::back_inserter(vints));

如果你想要值而不是键

std::ranges::copy(mapints | std::views::values, std::back_inserter(vints));

如果你不喜欢管道语法

std::ranges::copy(std::views::values(mapints), std::back_inserter(vints));