Estou trabalhando no STL efetivo no momento. O item 5 sugere que geralmente é preferível usar funções de membro de intervalo em vez de suas contrapartes de elemento único. No momento, desejo copiar todos os valores em um mapa (ou seja, não preciso das chaves) para um vetor.
Qual é a maneira mais limpa de fazer isso?
c++
stl
containers
Gilad Naor
fonte
fonte
Respostas:
Você não pode usar facilmente um intervalo aqui porque o iterador que você obtém de um mapa se refere a um std :: pair, onde os iteradores que você usaria para inserir em um vetor se referem a um objeto do tipo armazenado no vetor, que é (se você estiver descartando a chave) não um par.
Eu realmente não acho que fica muito mais limpo do que o óbvio:
#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 ); } }
que provavelmente reescreveria como uma função de modelo se fosse usá-la mais de uma vez. Algo como:
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 ); } }
fonte
Você provavelmente poderia usar
std::transform
para esse fim. Eu talvez prefira a versão Neils, dependendo do que for mais legível.Exemplo de xtofl (ver comentários):
#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) ); }
Muito genérico, lembre-se de dar crédito a ele se achar útil.
fonte
Velha pergunta, nova resposta. Com o C ++ 11, temos o novo loop for sofisticado:
for (const auto &s : schemas) names.push_back(s.first);
onde schemas é um
std::map
e names é umstd::vector
.Isso preenche o array (nomes) com chaves do mapa (esquemas); mude
s.first
paras.second
para obter uma matriz de valores.fonte
const auto &s
reserve()
e você terá outro ganho de desempenho. Com o advento do C ++ 11, essa agora deve ser a solução aceita!Se você estiver usando as bibliotecas boost , você pode usar boost :: bind para acessar o segundo valor do par da seguinte maneira:
#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) ); }
Esta solução é baseada em uma postagem de Michael Goldshteyn na lista de discussão boost .
fonte
#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;} );
Lamento não ter acrescentado nenhuma explicação - pensei que o código é tão simples que não requer nenhuma explicação. Então:
transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
esta função chama
unaryOperation
todos os itens doinputIterator
intervalo (beginInputRange
-endInputRange
). O valor da operação é armazenado emoutputIterator
.Se quisermos operar através do mapa inteiro - usamos map.begin () e map.end () como nosso intervalo de entrada. Queremos guardar os nossos valores do mapa para vetor - por isso temos de usar back_inserter em nosso vetor:
back_inserter(your_values_vector)
. O back_inserter é um outputIterator especial que empurra novos elementos no final de determinada coleção (como parâmetro). O último parâmetro é unaryOperation - leva apenas um parâmetro - o valor de inputIterator. Portanto, podemos usar lambda[](auto &kv) { [...] }
:, onde & kv é apenas uma referência para o par do item do mapa. Portanto, se quisermos retornar apenas os valores dos itens do mapa, podemos simplesmente retornar kv.segundo:[](auto &kv) { return kv.second; }
Acho que isso explica todas as dúvidas.
fonte
Usando lambdas, pode-se fazer o seguinte:
{ 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); }); }
fonte
Aqui está o que eu faria.
Também usaria uma função de template para tornar a construção de select2nd mais fácil.
#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) ); }
fonte
Uma maneira é usar o functor:
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); }
fonte
Por que não:
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; }
uso:
auto vec = MapValuesAsVector (anymap);
fonte
Eu pensei que deveria ser
std::transform( map.begin(), map.end(), std::back_inserter(vec), boost::bind(&MapT::value_type::first,_1) );
fonte
Devemos usar a função de transformação do algoritmo STL, o último parâmetro da função de transformação pode ser um objeto de função, ponteiro de função ou uma função lambda que converte item de mapa em item de vetor. Este mapa de caso possui itens com par de tipos que precisam ser convertidos em itens com tipo interno para vetor. Aqui está minha solução para usar a função lambda:
#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; });
fonte
Surpreso que ninguém mencionou a solução mais óbvia , use o construtor 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()); }
fonte