Это один из возможных вариантов выхода:
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 не включает для нас функцию-член.)
Единственное, что я бы добавил, это keys.reserve(m.size());.





Ваше решение в порядке, но вы можете использовать итератор для этого:
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. Ваш ответ я предпочитаю :)
@Jere - Вы действительно работали с BOOST_FOREACH? Код, который вы предлагаете, совершенно неправильный
Мануэль верен, синтаксис для итерации BOOST_FOREACH по карте состоит в том, чтобы сначала определить тип элемента без запятых (т.е. пара typedef <const int, int> element_type;), а затем выполнить итерацию с использованием фактических элементов (например, BOOST_FOREACH (element_type & e, m)
@Jamie - это другой способ, но в документации по усилению показано указание переменной и ее типа до BOOST_FOREACH, если тип содержит запятую. Они также показывают его определение типа. Итак, я сбит с толку, что не так с моим кодом?
@ Мануэль - извини, я только что видел твой ответ. Можете указать, что не так?
@ Jere.Jones, ваш код неверен, потому что ваша переменная является итератором. Вся суть BOOST_FOREACH заключается в том, что вам не нужны итераторы, вы просто получаете элементы один за другим. См. Мой предыдущий комментарий или этот дамп кода pastie.org/1045222
@Manual - Боже мой, как только я прочитал твое первое предложение, я ударил себя по голове. Спасибо! Я исправил это, чтобы не сбивать с толку будущих посетителей.
Любопытно, не имеет ли смысл предварительно задать вектор, чтобы предотвратить изменение размера?
Теперь, когда доступен C++ 11, я считаю, что метод, описанный ниже Хуаном, лучше: // C++ 0x too std :: map <int, int> mapints; std :: vector <int> винты; для (auto imap: mapints) vints.push_back (imap.first);
Не забудьте сделать v.reserve(m.size()), чтобы избежать изменения размера вектора во время передачи.
@BrianWhite Это плохой совет. Страуструп рекомендует не вызывать резерв (stroustrup.com/bs_faq2.html).
@ Étienne, его наблюдение по этой ссылке (на самом деле не рекомендация) относится к контексту «постепенного роста стоимости std :: vector». В этом случае он переходит к известному фиксированному размеру, поэтому я думаю, что это только преимущество.
@Brian White, вы также можете замедлить код, явно вызвав резерв, в зависимости от размера карты. Страуструп рекомендует не называть его явно, потому что это действительно крошечная оптимизация скорости с разумной реализацией std :: vector, он также рекомендует это в своей книге на С ++ «Язык программирования С ++».
Я думаю, что 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: это зависит от того, для чего вы его используете.
Исходя из этого, у нас не должно быть empty(), потому что он может быть реализован как size() == 0.
Что сказал @ gd1. Хотя в классе не должно быть много функциональной избыточности, настаивать на абсолютном нуле - не лучшая идея, ИМО - по крайней мере, до тех пор, пока C++ не позволит нам «благословить» свободные функции в методы.
В более старых версиях C++ были контейнеры, для которых empty () и size () могли разумно иметь разные гарантии производительности, и я думаю, что спецификация была достаточно свободной, чтобы разрешить это (в частности, связанные списки, которые предлагали splice () с постоянным временем) . Таким образом, их разъединение имело смысл. Однако я не думаю, что это несоответствие допустимо.
Я согласен. C++ рассматривает std::map<T,U> как контейнер пар. В Python dict действует как его ключи при повторении, но позволяет вам сказать d.items(), чтобы получить поведение C++. Python также предоставляет d.values(). std::map<T,U>, безусловно, может предоставить методы keys() и values(), которые возвращают объект с begin() и end(), которые предоставляют итераторы по ключам и значениям.
В C# есть такой метод. Я думаю, что наличие такой функции - это просто образ мышления (теория типов, объектно-ориентированное и функциональное мышление). Мне кажется, что DrPizza имеет императивную и ориентированную на производительность точку зрения (то есть, в конце концов, это всегда то, что происходит после компиляции и после внешнего интерфейса). В двух словах: должны ли мы мыслить как математики (с концепциями) или как механики (с тем, что / как делать)?
Лучшее решение 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 и обратные итераторы, если / когда это необходимо.
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); } Намного лучше, чем загадочное преобразование. Даже в плане производительности. Этот лучше.
Вы также можете зарезервировать размер клавиш здесь, если хотите сопоставимую производительность. используйте преобразование, если хотите избежать цикла for.
просто хочу добавить - можно использовать [] (const auto & pair)
@ ivan.ukr какой компилятор вы используете? Этот синтаксис здесь недопустим: 'const auto &': параметр не может иметь тип, содержащий 'auto'
Visual C++ 2015 Update 1, у меня также работает с Visual C++ 2015 Update 2
@ ivan.ukr автоматический параметр в лямбде - это C++ 14
@Jagannath Как ваша версия более производительна, если в этой также есть keys.reserve()? Просто любопытно.
Для этого есть адаптер диапазона повышения:
#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
Какая жалость. Boost 1.42 был выпущен в феврале 2010 года, за 2,5 года до Squeeze.
На этом этапе не следует ли Squeeze Updates и / или репозиторий backports предлагать Boost 1.44?
какой заголовок повышения определяется в?
См. Связанный документ, он определен в boost/range/adaptor/map.hpp
//c++0x too
std::map<int,int> mapints;
std::vector<int> vints;
for(auto const& imap: mapints)
vints.push_back(imap.first);
В вопросе нет тега C++ 0x.
Хороший. Забудьте о it = ...begin(); it != ...end. Самым красивым, конечно, было бы std :: map с методом keys (), возвращающим этот вектор ...
@BenHymers: Мне кажется, этот ответ был дан на answered Mar 13 '12 at 22:33, то есть через несколько месяцев после C++ 11 стал C++.
@phresnel: Хороший звонок, не знаю, какое число я читал. Учтите, что это не самая важная часть моего комментария; вопрос был задан в 2008 году, поэтому ответ C++ 11, вероятно, нежелателен! (ну, любой ответ с опозданием на 4 года, вероятно, бесполезен для спрашивающего ...)
@BenHymers, но это полезно для всех, кто читает вопрос сейчас, в этом и заключается суть SO - не только помощь спрашивающему, но и всем остальным.
for (auto & imap) более точен, потому что нет операции копирования.
@StudentT, еще лучше, for(auto const & imap : mapints).
Я предпочитаю for (auto&& imap : mapints). См. edmundv.home.xs4all.nl/blog/2014/01/28/…
Кроме того, некоторым из нас приходится работать в средах без поддержки C++ 11 -_-
Если вы планируете использовать это эффективно, вам следует добавить vints.reserve( mapints.size() ); перед циклом.
Здесь человек из будущего. Что касается комментария @ user283145, теперь C++ == C++ 17, и это тоже не продлится слишком долго. Я, например, ценю, когда в ответах указывается языковая совместимость. «C++» - это язык, который изменился и будет продолжать меняться.
@Antonio Я откатил ваше редактирование, явный вызов резерва не рекомендуется и в большинстве случаев является признаком преждевременной оптимизации (см. stroustrup.com/bs_faq2.html)
@ Étienne. Я согласен, что это вопрос стиля, хотя в зависимости от платформы, как мне кажется, распределение затрат обходится более или менее дорого (в Android, по моему опыту). Я считаю хорошей практикой предоставление компилятору любой доступной информации для оптимизации, в данном случае: «вектор вырастет, по крайней мере, до этого размера».
Немного с ++ 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()); для повышения эффективности.
Я считаю, что метод преобразования на самом деле требует больше кода, чем цикл for.
const можно поставить за типом! Я почти забыл об этом.
Немного похож на один из примеров здесь, упрощенный с точки зрения использования 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() означает удвоение возврата размера вектора. Пожалуйста, исправьте, чтобы избавить кого-то от головной боли :(
На основе решения @ 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 Спасибо. Действительно, std::ignore предназначен для использования с std::tie, но не со структурными привязками. Я обновил свой код.
На примере атомной карты
#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
вы можете сделать это, что легче понять.
// 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);
}
ваше решение лучшее ...