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

ваше решение лучшее ...

linello 09.04.2013 14:15

Единственное, что я бы добавил, это keys.reserve(m.size());.

Galik 04.04.2018 21:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
272
2
383 894
20
Перейти к ответу Данный вопрос помечен как решенный

Ответы 20

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

Поймите, я бы вернулся сюда после поиска в Google. Ваш ответ я предпочитаю :)

mpen 15.04.2009 00:15

@Jere - Вы действительно работали с BOOST_FOREACH? Код, который вы предлагаете, совершенно неправильный

Manuel 05.03.2010 22:25

Мануэль верен, синтаксис для итерации BOOST_FOREACH по карте состоит в том, чтобы сначала определить тип элемента без запятых (т.е. пара typedef <const int, int> element_type;), а затем выполнить итерацию с использованием фактических элементов (например, BOOST_FOREACH (element_type & e, m)

Jamie Cook 09.07.2010 13:19

@Jamie - это другой способ, но в документации по усилению показано указание переменной и ее типа до BOOST_FOREACH, если тип содержит запятую. Они также показывают его определение типа. Итак, я сбит с толку, что не так с моим кодом?

Jere.Jones 09.07.2010 20:56

@ Мануэль - извини, я только что видел твой ответ. Можете указать, что не так?

Jere.Jones 09.07.2010 20:56

@ Jere.Jones, ваш код неверен, потому что ваша переменная является итератором. Вся суть BOOST_FOREACH заключается в том, что вам не нужны итераторы, вы просто получаете элементы один за другим. См. Мой предыдущий комментарий или этот дамп кода pastie.org/1045222

Jamie Cook 15.07.2010 10:36

@Manual - Боже мой, как только я прочитал твое первое предложение, я ударил себя по голове. Спасибо! Я исправил это, чтобы не сбивать с толку будущих посетителей.

Jere.Jones 19.07.2010 20:51

Любопытно, не имеет ли смысл предварительно задать вектор, чтобы предотвратить изменение размера?

Alan 17.10.2013 02:34

Теперь, когда доступен C++ 11, я считаю, что метод, описанный ниже Хуаном, лучше: // C++ 0x too std :: map <int, int> mapints; std :: vector <int> винты; для (auto imap: mapints) vints.push_back (imap.first);

user561749 15.11.2015 19:30

Не забудьте сделать v.reserve(m.size()), чтобы избежать изменения размера вектора во время передачи.

Brian White 21.03.2017 17:34

@BrianWhite Это плохой совет. Страуструп рекомендует не вызывать резерв (stroustrup.com/bs_faq2.html).

Étienne 31.08.2020 11:40

@ Étienne, его наблюдение по этой ссылке (на самом деле не рекомендация) относится к контексту «постепенного роста стоимости std :: vector». В этом случае он переходит к известному фиксированному размеру, поэтому я думаю, что это только преимущество.

Brian White 02.09.2020 23:09

@Brian White, вы также можете замедлить код, явно вызвав резерв, в зависимости от размера карты. Страуструп рекомендует не называть его явно, потому что это действительно крошечная оптимизация скорости с разумной реализацией std :: vector, он также рекомендует это в своей книге на С ++ «Язык программирования С ++».

Étienne 02.09.2020 23:17

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

(I'm always wondering why std::map does not include a member function for us to do so.)

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

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

Существуют и другие причины, помимо эффективности библиотеки для предоставления метода, такие как функциональность «с батарейками» и согласованный, инкапсулированный API. Хотя надо признать, что ни один из этих терминов не описывает STL особенно хорошо :) Re. непонятно, зачем это полезно - правда? Я думаю, что довольно очевидно, почему перечисление доступных ключей - это полезная вещь, которую можно делать с помощью map / dict: это зависит от того, для чего вы его используете.

andybuckley 16.12.2012 18:19

Исходя из этого, у нас не должно быть empty(), потому что он может быть реализован как size() == 0.

gd1 01.10.2015 11:49

Что сказал @ gd1. Хотя в классе не должно быть много функциональной избыточности, настаивать на абсолютном нуле - не лучшая идея, ИМО - по крайней мере, до тех пор, пока C++ не позволит нам «благословить» свободные функции в методы.

einpoklum 02.02.2016 22:15

В более старых версиях C++ были контейнеры, для которых empty () и size () могли разумно иметь разные гарантии производительности, и я думаю, что спецификация была достаточно свободной, чтобы разрешить это (в частности, связанные списки, которые предлагали splice () с постоянным временем) . Таким образом, их разъединение имело смысл. Однако я не думаю, что это несоответствие допустимо.

DrPizza 09.04.2016 02:11

Я согласен. C++ рассматривает std::map<T,U> как контейнер пар. В Python dict действует как его ключи при повторении, но позволяет вам сказать d.items(), чтобы получить поведение C++. Python также предоставляет d.values(). std::map<T,U>, безусловно, может предоставить методы keys() и values(), которые возвращают объект с begin() и end(), которые предоставляют итераторы по ключам и значениям.

Ben 10.07.2017 20:02

В C# есть такой метод. Я думаю, что наличие такой функции - это просто образ мышления (теория типов, объектно-ориентированное и функциональное мышление). Мне кажется, что DrPizza имеет императивную и ориентированную на производительность точку зрения (то есть, в конце концов, это всегда то, что происходит после компиляции и после внешнего интерфейса). В двух словах: должны ли мы мыслить как математики (с концепциями) или как механики (с тем, что / как делать)?

Soleil 02.10.2020 19:18

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

Я оставляю читателю возможность также создать const_iterator и обратные итераторы, если / когда это необходимо.

Marius 05.03.2010 22:29

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

На мой взгляд, ничего хорошего в этом нет. std :: vector <int> keys; keys.reserve (m_Inputs.size ()); для (автоматическое значение ключа: m_Inputs) {keys.push_back (keyValue.first); } Намного лучше, чем загадочное преобразование. Даже в плане производительности. Этот лучше.

Jagannath 11.06.2012 18:59

Вы также можете зарезервировать размер клавиш здесь, если хотите сопоставимую производительность. используйте преобразование, если хотите избежать цикла for.

DanDan 15.06.2012 00:35

просто хочу добавить - можно использовать [] (const auto & pair)

ivan.ukr 02.03.2016 18:04

@ ivan.ukr какой компилятор вы используете? Этот синтаксис здесь недопустим: 'const auto &': параметр не может иметь тип, содержащий 'auto'

Gobe 20.04.2016 23:37

Visual C++ 2015 Update 1, у меня также работает с Visual C++ 2015 Update 2

ivan.ukr 26.04.2016 03:22

@ ivan.ukr автоматический параметр в лямбде - это C++ 14

roalz 14.10.2017 00:24

@Jagannath Как ваша версия более производительна, если в этой также есть keys.reserve()? Просто любопытно.

legends2k 26.05.2020 10:57

Для этого есть адаптер диапазона повышения:

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

К сожалению, похоже, что boost::adaptors недоступен до Boost 1.43. Текущая стабильная версия Debian (Squeeze) предлагает только Boost 1.42

Mickaël Le Baillif 23.11.2012 16:45

Какая жалость. Boost 1.42 был выпущен в феврале 2010 года, за 2,5 года до Squeeze.

Alastair 01.12.2012 00:40

На этом этапе не следует ли Squeeze Updates и / или репозиторий backports предлагать Boost 1.44?

Luis Machuca 28.03.2013 18:16

какой заголовок повышения определяется в?

James Wierzba 14.04.2016 22:41

См. Связанный документ, он определен в boost/range/adaptor/map.hpp

Alastair 15.04.2016 16:57

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

В вопросе нет тега C++ 0x.

Jagannath 11.06.2012 18:58

Хороший. Забудьте о it = ...begin(); it != ...end. Самым красивым, конечно, было бы std :: map с методом keys (), возвращающим этот вектор ...

masterxilo 19.05.2013 03:28

@BenHymers: Мне кажется, этот ответ был дан на answered Mar 13 '12 at 22:33, то есть через несколько месяцев после C++ 11 стал C++.

Sebastian Mach 22.07.2013 18:31

@phresnel: Хороший звонок, не знаю, какое число я читал. Учтите, что это не самая важная часть моего комментария; вопрос был задан в 2008 году, поэтому ответ C++ 11, вероятно, нежелателен! (ну, любой ответ с опозданием на 4 года, вероятно, бесполезен для спрашивающего ...)

Ben Hymers 23.07.2013 13:17

@BenHymers, но это полезно для всех, кто читает вопрос сейчас, в этом и заключается суть SO - не только помощь спрашивающему, но и всем остальным.

Luchian Grigore 14.12.2013 05:16

for (auto & imap) более точен, потому что нет операции копирования.

SmallChess 23.06.2015 09:20

@StudentT, еще лучше, for(auto const & imap : mapints).

cp.engr 23.10.2015 19:58

Я предпочитаю for (auto&& imap : mapints). См. edmundv.home.xs4all.nl/blog/2014/01/28/…

Rotsiser Mho 25.10.2015 07:49

Кроме того, некоторым из нас приходится работать в средах без поддержки C++ 11 -_-

Daniel 04.04.2016 16:56

Если вы планируете использовать это эффективно, вам следует добавить vints.reserve( mapints.size() ); перед циклом.

DrPepperJo 14.02.2017 02:53

Здесь человек из будущего. Что касается комментария @ user283145, теперь C++ == C++ 17, и это тоже не продлится слишком долго. Я, например, ценю, когда в ответах указывается языковая совместимость. «C++» - это язык, который изменился и будет продолжать меняться.

chessofnerd 30.11.2018 20:54

@Antonio Я откатил ваше редактирование, явный вызов резерва не рекомендуется и в большинстве случаев является признаком преждевременной оптимизации (см. stroustrup.com/bs_faq2.html)

Étienne 31.08.2020 11:46

@ Étienne. Я согласен, что это вопрос стиля, хотя в зависимости от платформы, как мне кажется, распределение затрат обходится более или менее дорого (в Android, по моему опыту). Я считаю хорошей практикой предоставление компилятору любой доступной информации для оптимизации, в данном случае: «вектор вырастет, по крайней мере, до этого размера».

Antonio 02.09.2020 19:49

Немного с ++ 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

Ответ @ 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.

Вы можете добавить keys.reserve(map_in.size()); для повышения эффективности.

Galik 04.04.2018 21:18

Я считаю, что метод преобразования на самом деле требует больше кода, чем цикл for.

user1633272 05.11.2018 11:00

const можно поставить за типом! Я почти забыл об этом.

Zhang 06.05.2019 11:58

Немного похож на один из примеров здесь, упрощенный с точки зрения использования 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);

Эй, я знаю, что это старый ответ, но он тоже неправильный. Инициализация размером map.size() означает удвоение возврата размера вектора. Пожалуйста, исправьте, чтобы избавить кого-то от головной боли :(

thc 04.07.2019 02:44

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

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

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

Я не думаю, что std::ignore можно использовать в структурированных привязках таким образом. Я получаю ошибку компиляции. Достаточно просто использовать обычную переменную, например. ignored, который просто не используется.

j b 31.03.2020 13:02

@ j-b Спасибо. Действительно, std::ignore предназначен для использования с std::tie, но не со структурными привязками. Я обновил свой код.

Madiyar 12.04.2020 14:23

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

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

вы можете сделать это, что легче понять.

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

Другие вопросы по теме