В данный момент прорабатываю эффективный STL. Правило 5 предполагает, что обычно предпочтительнее использовать функции-члены диапазона вместо их одноэлементных аналогов. В настоящее время я хочу скопировать все значения на карте (т.е. мне не нужны ключи) в вектор.
Как лучше всего это сделать?
c++
stl
containers
Гилад Наор
источник
источник
Ответы:
Вы не можете легко использовать здесь диапазон, потому что итератор, который вы получаете от карты, ссылается на std :: pair, где итераторы, которые вы использовали бы для вставки в вектор, ссылаются на объект типа, хранящегося в векторе, который является (если вы сбрасываете ключ) не пара.
Я действительно не думаю, что это становится намного чище, чем очевидное:
#include <map> #include <vector> #include <string> using namespace std; int main() { typedef map <string, int> MapType; MapType m; vector <int> v; // populate map somehow for( MapType::iterator it = m.begin(); it != m.end(); ++it ) { v.push_back( it->second ); } }
которую я, вероятно, переписал бы как функцию шаблона, если бы собирался использовать ее более одного раза. Что-то типа:
template <typename M, typename V> void MapToVec( const M & m, V & v ) { for( typename M::const_iterator it = m.begin(); it != m.end(); ++it ) { v.push_back( it->second ); } }
источник
Вероятно, вы могли бы использовать
std::transform
для этой цели. Я, возможно, предпочел бы версию Нилса, в зависимости от того, что более читабельно.Пример от xtofl (см. Комментарии):
#include <map> #include <vector> #include <algorithm> #include <iostream> template< typename tPair > struct second_t { typename tPair::second_type operator()( const tPair& p ) const { return p.second; } }; template< typename tMap > second_t< typename tMap::value_type > second( const tMap& m ) { return second_t< typename tMap::value_type >(); } int main() { std::map<int,bool> m; m[0]=true; m[1]=false; //... std::vector<bool> v; std::transform( m.begin(), m.end(), std::back_inserter( v ), second(m) ); std::transform( m.begin(), m.end(), std::ostream_iterator<bool>( std::cout, ";" ), second(m) ); }
Очень общий, не забудьте отдать должное, если сочтете это полезным.
источник
Старый вопрос, новый ответ. В C ++ 11 появился новый модный цикл for:
for (const auto &s : schemas) names.push_back(s.first);
где схемы - это,
std::map
а имена - этоstd::vector
.Это заполняет массив (имена) ключами из карты (схем); измените
s.first
на,s.second
чтобы получить массив значений.источник
const auto &s
reserve()
и вы получите еще один прирост производительности. С появлением C ++ 11 это теперь должно быть приемлемым решением!Если вы используете библиотеки boost , вы можете использовать boost :: bind для доступа ко второму значению пары следующим образом:
#include <string> #include <map> #include <vector> #include <algorithm> #include <boost/bind.hpp> int main() { typedef std::map<std::string, int> MapT; typedef std::vector<int> VecT; MapT map; VecT vec; map["one"] = 1; map["two"] = 2; map["three"] = 3; map["four"] = 4; map["five"] = 5; std::transform( map.begin(), map.end(), std::back_inserter(vec), boost::bind(&MapT::value_type::second,_1) ); }
Это решение основано на сообщении Майкла Гольдштейна в списке рассылки boost .
источник
#include <algorithm> // std::transform #include <iterator> // std::back_inserter std::transform( your_map.begin(), your_map.end(), std::back_inserter(your_values_vector), [](auto &kv){ return kv.second;} );
Извините, что я не добавил никаких объяснений - я думал, что код настолько прост, что не требует никаких объяснений. Так:
transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
эта функция вызывает
unaryOperation
каждый элемент изinputIterator
диапазона (beginInputRange
-endInputRange
). Значение операции сохраняется вoutputIterator
.Если мы хотим работать со всей картой - мы используем map.begin () и map.end () в качестве диапазона ввода. Мы хотим сохранить наши ценности карты в векторе - поэтому мы должны использовать back_inserter на нашем векторе:
back_inserter(your_values_vector)
. Back_inserter - это специальный outputIterator, который подталкивает новые элементы в конец данной (как параметр) коллекции. Последний параметр - unaryOperation - принимает только один параметр - значение inputIterator. Таким образом, мы можем использовать лямбда :[](auto &kv) { [...] }
, где & kv - это просто ссылка на пару элементов карты. Поэтому, если мы хотим вернуть только значения элементов карты, мы можем просто вернуть kv.second:[](auto &kv) { return kv.second; }
Думаю, это объясняет любые сомнения.
источник
Используя лямбды, можно сделать следующее:
{ std::map<std::string,int> m; std::vector<int> v; v.reserve(m.size()); std::for_each(m.begin(),m.end(), [&v](const std::map<std::string,int>::value_type& p) { v.push_back(p.second); }); }
источник
Вот что бы я сделал.
Также я бы использовал функцию шаблона, чтобы упростить построение select2nd.
#include <map> #include <vector> #include <algorithm> #include <memory> #include <string> /* * A class to extract the second part of a pair */ template<typename T> struct select2nd { typename T::second_type operator()(T const& value) const {return value.second;} }; /* * A utility template function to make the use of select2nd easy. * Pass a map and it automatically creates a select2nd that utilizes the * value type. This works nicely as the template functions can deduce the * template parameters based on the function parameters. */ template<typename T> select2nd<typename T::value_type> make_select2nd(T const& m) { return select2nd<typename T::value_type>(); } int main() { std::map<int,std::string> m; std::vector<std::string> v; /* * Please note: You must use std::back_inserter() * As transform assumes the second range is as large as the first. * Alternatively you could pre-populate the vector. * * Use make_select2nd() to make the function look nice. * Alternatively you could use: * select2nd<std::map<int,std::string>::value_type>() */ std::transform(m.begin(),m.end(), std::back_inserter(v), make_select2nd(m) ); }
источник
Один из способов - использовать функтор:
template <class T1, class T2> class CopyMapToVec { public: CopyMapToVec(std::vector<T2>& aVec): mVec(aVec){} bool operator () (const std::pair<T1,T2>& mapVal) const { mVec.push_back(mapVal.second); return true; } private: std::vector<T2>& mVec; }; int main() { std::map<std::string, int> myMap; myMap["test1"] = 1; myMap["test2"] = 2; std::vector<int> myVector; //reserve the memory for vector myVector.reserve(myMap.size()); //create the functor CopyMapToVec<std::string, int> aConverter(myVector); //call the functor std::for_each(myMap.begin(), myMap.end(), aConverter); }
источник
Почему нет:
template<typename K, typename V> std::vector<V> MapValuesAsVector(const std::map<K, V>& map) { std::vector<V> vec; vec.reserve(map.size()); std::for_each(std::begin(map), std::end(map), [&vec] (const std::map<K, V>::value_type& entry) { vec.push_back(entry.second); }); return vec; }
Применение:
auto vec = MapValuesAsVector (anymap);
источник
Я думал так должно быть
std::transform( map.begin(), map.end(), std::back_inserter(vec), boost::bind(&MapT::value_type::first,_1) );
источник
Мы должны использовать функцию преобразования из алгоритма STL, последним параметром функции преобразования может быть объект функции, указатель функции или лямбда-функция, которая преобразует элемент карты в элемент вектора. Эта карта случаев имеет элементы с парой типов, которые необходимо преобразовать в элемент с типом int для вектора. Вот мое решение, в котором я использую лямбда-функцию:
#include <algorithm> // for std::transform #include <iterator> // for back_inserted // Map of pair <int, string> need to convert to vector of string std::map<int, std::string> mapExp = { {1, "first"}, {2, "second"}, {3, "third"}, {4,"fourth"} }; // vector of string to store the value type of map std::vector<std::string> vValue; // Convert function std::transform(mapExp.begin(), mapExp.end(), std::back_inserter(vValue), [](const std::pair<int, string> &mapItem) { return mapItem.second; });
источник
Удивлен, что никто не упомянул наиболее очевидное решение - использовать конструктор std :: vector.
template<typename K, typename V> std::vector<std::pair<K,V>> mapToVector(const std::unordered_map<K,V> &map) { return std::vector<std::pair<K,V>>(map.begin(), map.end()); }
источник