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


person Owen    schedule 21.09.2008    source источник
comment
ваше решение лучшее ...   -  person linello    schedule 09.04.2013
comment
Единственное, что я мог бы добавить, это keys.reserve(m.size());.   -  person Galik    schedule 04.04.2018


Ответы (22)


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

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

person Jere.Jones    schedule 21.09.2008
comment
Поймите, я бы вернулся сюда после поиска в Google. Ваш ответ, я предпочитаю :) - person mpen; 15.04.2009
comment
@Jere - Ты действительно работал с BOOST_FOREACH? Код, который вы предлагаете, совершенно неверен - person Manuel; 05.03.2010
comment
Мануэль прав, синтаксис для итерации BOOST_FOREACH по карте состоит в том, чтобы сначала определить тип элемента без запятых (т.е. пара typedef ‹const int, int› element_type;), а затем выполнить итерацию с использованием фактических элементов (например, BOOST_FOREACH (element_type & e, m) - person Jamie Cook; 09.07.2010
comment
@Jamie - это другой способ, но в документации по усилению показано указание переменной и ее типа до BOOST_FOREACH, если тип содержит запятую. Они также показывают его определение типа. Итак, я сбит с толку, что не так с моим кодом? - person Jere.Jones; 09.07.2010
comment
@ Мануэль - извини, я только что видел твой ответ. Можете указать, что не так? - person Jere.Jones; 09.07.2010
comment
@ Jere.Jones, ваш код неверен, потому что ваша переменная является итератором. Вся суть BOOST_FOREACH заключается в том, что вам не нужны итераторы, вы просто получаете элементы один за другим. См. Мой предыдущий комментарий или этот дамп кода pastie.org/1045222 - person Jamie Cook; 15.07.2010
comment
@Manual - Боже мой, как только я прочитал твое первое предложение, я ударил себя по голове. Спасибо! Я исправил это, чтобы не сбивать с толку будущих посетителей. - person Jere.Jones; 19.07.2010
comment
Любопытно, не имеет ли смысл предварительно задать вектор, чтобы предотвратить изменение размера? - person Alan; 17.10.2013
comment
Теперь, когда доступен C ++ 11, я считаю, что метод, описанный ниже Хуаном, лучше: // c ++ 0x too std :: map ‹int, int› mapints; std :: vector ‹int› винты; для (auto imap: mapints) vints.push_back (imap.first); - person user561749; 15.11.2015
comment
Не забудьте сделать v.reserve(m.size()), чтобы избежать изменения размера вектора во время передачи. - person Brian White; 21.03.2017
comment
@BrianWhite Это плохой совет. Страуструп рекомендует не вызывать резерв (stroustrup.com/bs_faq2.html). - person Étienne; 31.08.2020
comment
@ Étienne, его наблюдение по этой ссылке (на самом деле не рекомендация) связано с постепенным увеличением стоимости std :: vector. В этом случае он переходит к известному фиксированному размеру, поэтому я думаю, что это только преимущество. - person Brian White; 02.09.2020
comment
@Brian White, вы также можете замедлить код, явно вызвав резерв, в зависимости от размера карты. Страуструп рекомендует не называть его явно, потому что это действительно крошечная оптимизация скорости с разумной реализацией std :: vector, он также рекомендует это в своей книге C ++ о языке программирования C ++. - person Étienne; 02.09.2020

Существует адаптер диапазона повышения для этой цели:

#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 ​​для извлечения значений.

person Alastair    schedule 05.03.2012
comment
К сожалению, похоже, что boost::adaptors недоступен до Boost 1.43. Текущая стабильная версия Debian (Squeeze) предлагает только Boost 1.42 - person Mickaël Le Baillif; 23.11.2012
comment
Какая жалость. Boost 1.42 был выпущен в феврале 2010 года, за 2,5 года до Squeeze. - person Alastair; 01.12.2012
comment
На этом этапе не следует ли Squeeze Updates и / или репозиторий backports предлагать Boost 1.44? - person Luis Machuca; 28.03.2013
comment
какой заголовок повышения определяется в? - person James Wierzba; 14.04.2016
comment
См. Связанный документ, он определен в boost/range/adaptor/map.hpp - person Alastair; 15.04.2016

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;});
person DanDan    schedule 08.05.2010
comment
На мой взгляд, ничего хорошего в этом нет. std :: vector ‹int› ключи; keys.reserve (m_Inputs.size ()); для (автоматическое значение ключа: m_Inputs) {keys.push_back (keyValue.first); } Намного лучше, чем загадочное преобразование. Даже в плане производительности. Этот лучше. - person Jagannath; 11.06.2012
comment
Вы также можете зарезервировать размер клавиш здесь, если хотите сопоставимую производительность. используйте преобразование, если хотите избежать цикла for. - person DanDan; 15.06.2012
comment
просто хочу добавить - можно использовать [] (const auto & pair) - person ivan.ukr; 02.03.2016
comment
@ ivan.ukr какой компилятор вы используете? Этот синтаксис здесь недопустим: 'const auto &': параметр не может иметь тип, содержащий 'auto' - person Gobe; 20.04.2016
comment
Visual C ++ 2015 Update 1, у меня также работает с Visual C ++ 2015 Update 2 - person ivan.ukr; 26.04.2016
comment
@ ivan.ukr автоматический параметр в лямбде - это c ++ 14 - person roalz; 14.10.2017
comment
@Jagannath Как ваша версия более производительна, если в этой тоже есть keys.reserve()? Просто любопытно. - person legends2k; 26.05.2020

На основе решения @ rusty-parks, но в С ++ 17:

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

for (const auto& [key, _] : items) {
    itemKeys.push_back(key);
}
person Madiyar    schedule 03.05.2019
comment
Я не думаю, что std::ignore можно использовать в структурированных привязках таким образом. Я получаю ошибку компиляции. Достаточно просто использовать обычную переменную, например. ignored к которому просто не привыкаешь. - person j b; 31.03.2020
comment
@ j-b Спасибо. Действительно, std::ignore предназначен для использования с std::tie, но не со структурными привязками. Я обновил свой код. - person Madiyar; 12.04.2020

Ответ @ DanDan с использованием С ++ 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.

person James Hirschorn    schedule 16.09.2016
comment
Вы можете добавить keys.reserve(map_in.size()); для повышения эффективности. - person Galik; 04.04.2018
comment
Я считаю, что метод преобразования на самом деле требует больше кода, чем цикл for. - person user1633272; 05.11.2018
comment
const можно поставить за типом! Я почти забыл об этом. - person Zhang; 06.05.2019

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

person Chris Jester-Young    schedule 21.09.2008

Ваше решение в порядке, но вы можете использовать итератор для этого:

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
}
person Brian R. Bondy    schedule 21.09.2008

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

person paxos1977    schedule 21.09.2008

Немного с ++ 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;
}
person Rusty Parks    schedule 10.03.2016

Вот хороший шаблон функции, использующий магию 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

person Clemens Sielaff    schedule 10.08.2016
comment
Это лучший вариант только потому, что это единственное решение, в котором сначала нужно зарезервировать размер. - person wKavey; 09.04.2021

Кроме того, если у вас есть Boost, используйте transform_iterator, чтобы избежать создания временной копии ключей.

person Marcelo Cantos    schedule 21.09.2008

Вы можете использовать универсальный boost :: transform_iterator. Transform_iterator позволяет вам преобразовывать повторяющиеся значения, например, в нашем случае, когда вы хотите иметь дело только с ключами, а не со значениями. См. http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/transform_iterator.html#example

person amit    schedule 21.09.2008

Лучшее решение STL без sgi и без повышения - это расширить 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];
person Marius    schedule 05.03.2010
comment
Я оставлю читателю возможность также создать const_iterator и обратные итераторы, если / когда это необходимо. - person Marius; 05.03.2010


Я нашел следующие три строчки кода как самый простой способ:

// save keys in vector

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

Это сокращенная версия первого способа этого ответа.

person Chrissi    schedule 30.07.2020

Еще один способ использования C ++ 20

В библиотеке диапазонов есть представление ключей, которое извлекает первый элемент в виде пары / кортежа:

#include <ranges>

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

Стоит упомянуть два связанных взгляда:

  1. values ​​- чтобы получить значения в карте (2-й элемент в парном / кортежном типе)
  2. elements - чтобы получить n-е элементы в кортежеподобном типе
person Mercury Dime    schedule 23.06.2021

На примере атомной карты

#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;
}
person Deniz Babat    schedule 24.03.2020

Следующий функтор извлекает набор ключей карты:

#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
person KaiserKatze    schedule 20.12.2020

Вы можете использовать get_map_keys () из библиотеки fplus:

#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;
}
person Jee lee    schedule 07.06.2021

Немного похож на один из примеров здесь, упрощен с 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);
person TarmoPikaro    schedule 14.04.2019
comment
Эй, я знаю, что это старый ответ, но он тоже неправильный. Инициализация размером map.size() означает удвоение возврата размера вектора. Пожалуйста, исправьте, чтобы избавить кого-то от головной боли :( - person thc; 04.07.2019

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

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

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

person DrPizza    schedule 21.09.2008
comment
Существуют и другие причины, помимо эффективности библиотеки для предоставления метода, такие как включенная функциональность батарей и согласованный, инкапсулированный API. Хотя надо признать, что ни один из этих терминов не описывает STL особенно хорошо :) Re. непонятно, зачем это полезно - правда? Я думаю, что довольно очевидно, почему перечисление доступных ключей - это полезная вещь, которую можно делать с помощью map / dict: это зависит от того, для чего вы его используете. - person andybuckley; 16.12.2012
comment
Исходя из этого, у нас не должно быть empty(), потому что это может быть реализовано как size() == 0. - person gd1; 01.10.2015
comment
Что сказал @ gd1. Хотя в классе не должно быть много функциональной избыточности, настаивать на абсолютном нуле - не лучшая идея, IMO - по крайней мере, до тех пор, пока C ++ не позволит нам добавить бесплатные функции в методы. - person einpoklum; 02.02.2016
comment
В более старых версиях C ++ были контейнеры, для которых empty () и size () могли разумно иметь разные гарантии производительности, и я думаю, что спецификация была достаточно свободной, чтобы разрешить это (в частности, связанные списки, которые предлагали splice () с постоянным временем) . Таким образом, их разъединение имело смысл. Однако я не думаю, что это несоответствие допустимо. - person DrPizza; 09.04.2016
comment
Я согласен. C ++ рассматривает std::map<T,U> как контейнер пар. В Python при повторении dict действует как его ключи, но позволяет вам сказать d.items(), чтобы получить поведение C ++. Python также предоставляет d.values(). std::map<T,U>, безусловно, может предоставить методы keys() и values(), которые возвращают объект, имеющий begin() и end(), которые предоставляют итераторы по ключам и значениям. - person Ben; 10.07.2017
comment
В c # есть такой метод. Я думаю, что наличие такой функции - это просто образ мышления (теория типов, объектно-ориентированное и функциональное мышление). Мне кажется, что DrPizza имеет императивную и ориентированную на производительность точку зрения (то есть, в конце концов, это всегда то, что происходит после компиляции и после внешнего интерфейса). В двух словах: должны ли мы мыслить как математики (с концепциями) или как механики (с тем, что / как делать)? - person Soleil; 02.10.2020

person    schedule
comment
В вопросе нет тега c ++ 0x. - person Jagannath; 11.06.2012
comment
Отлично. Забудьте о it = ...begin(); it != ...end. Самым красивым, конечно, было бы std :: map с методом keys (), возвращающим этот вектор ... - person masterxilo; 19.05.2013
comment
@BenHymers: Мне кажется, этот ответ был дан на answered Mar 13 '12 at 22:33, то есть через несколько месяцев после того, как C ++ 11 стал C ++. - person Sebastian Mach; 22.07.2013
comment
@phresnel: Хороший звонок, не знаю, какое число я читал. Учтите, что это не важная часть моего комментария; вопрос был задан в 2008 году, поэтому ответ C ++ 11, вероятно, нежелателен! (ну, любой ответ с опозданием на 4 года, вероятно, бесполезен для спрашивающего ...) - person Ben Hymers; 23.07.2013
comment
@BenHymers, но это полезно для всех, кто сейчас читает вопрос, в чем и заключается суть SO - не только помощь спрашивающему, но и всем остальным. - person Luchian Grigore; 14.12.2013
comment
for (auto & imap) более точен, потому что нет операции копирования. - person SmallChess; 23.06.2015
comment
@StudentT, еще лучше, for(auto const & imap : mapints). - person cp.engr; 23.10.2015
comment
Я предпочитаю for (auto&& imap : mapints). См. edmundv.home.xs4all.nl/blog/2014/01/28/ - person Rotsiser Mho; 25.10.2015
comment
Кроме того, некоторым из нас приходится работать в средах без поддержки C ++ 11 -_- - person Daniel; 04.04.2016
comment
Если вы планируете использовать это эффективно, вам следует добавить vints.reserve( mapints.size() ); перед циклом. - person DrPepperJo; 14.02.2017
comment
Человек из будущего здесь. Что касается комментария @ user283145, теперь C ++ == C ++ 17, и это тоже не продлится слишком долго. Я, например, ценю, когда в ответах указывается языковая совместимость. C ++ - это язык, который изменился и будет продолжать меняться. - person chessofnerd; 30.11.2018
comment
@Antonio Я откатил ваше редактирование, явный вызов резерва не рекомендуется и в большинстве случаев является признаком преждевременной оптимизации (см. stroustrup.com/bs_faq2.html) - person Étienne; 31.08.2020
comment
@ Étienne Я согласен, что это вопрос стиля, хотя, в зависимости от платформы, я заметил, что выделение ресурсов обходится более или менее дорого (в Android, по моему опыту). Я считаю хорошей практикой предоставление компилятору любой доступной информации для оптимизации, в данном случае: «вектор вырастет, по крайней мере, до этого размера». - person Antonio; 02.09.2020