Как получить все ключи (или значения) из 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 не включает в себя функцию-член для нас, чтобы сделать это.)

14 179

14 ответов:

пока ваше решение должно работать, это может быть трудно читать в зависимости от уровня квалификации ваших программистов. Кроме того, он перемещает функциональность от сайта вызова. Что может сделать обслуживание немного более трудным.

Я не уверен, что ваша цель-получить ключи в вектор или распечатать их в 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";
}

или даже проще, если вы используете Повышение:

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, потому что там меньше ввода, и это очень ясно о том, что он делает.

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

есть буст адаптер серии для этого:

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

существует аналогичный адаптер диапазона map_values для извлечения значений.

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

SGI STL имеет расширение под названием select1st. Жаль, что это не в стандартном STL!

Я думаю, что 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 может быть действительно чистым в других случаях.

ответ@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.Укр) мы можем заменить decltype(map_in)::value_type С auto.

ваше решение-это хорошо, но вы можете использовать итератор, чтобы сделать это:

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

лучшее решение без 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];

бит c++11 take:

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

(Мне всегда интересно, почему std:: map не включает в себя функцию-член для нас, чтобы сделать это.)

потому что он не может сделать это лучше вы можете сделать это. Если реализация метода не будет превосходить реализацию свободной функции, то в общем случае вы не должны писать метод; вы должны написать свободную функцию.

это также не сразу понятно, почему это полезно в любом случае.