Trabajando a través de STL efectivo en este momento. El ítem 5 sugiere que generalmente es preferible usar funciones de miembros de rango a sus contrapartes de un solo elemento. Actualmente deseo copiar todos los valores en un mapa (es decir, no necesito las claves) en un vector.
¿Cuál es la forma más limpia de hacer esto?
c++
stl
containers
Gilad Naor
fuente
fuente
Respuestas:
No puede usar fácilmente un rango aquí porque el iterador que obtiene de un mapa se refiere a un std :: pair, donde los iteradores que usaría para insertar en un vector se refieren a un objeto del tipo almacenado en el vector, que es (si descarta la clave) no un par.
Realmente no creo que se vuelva mucho más limpio que lo obvio:
#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 probablemente volvería a escribir como una función de plantilla si fuera a usarla más de una 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 ); } }
fuente
Probablemente podrías usar
std::transform
para ese propósito. Sin embargo, tal vez prefiera la versión de Neils, dependiendo de lo que sea más legible.Ejemplo de xtofl (ver comentarios):
#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) ); }
Muy genérico, recuerde darle crédito si lo encuentra útil.
fuente
Antigua pregunta, nueva respuesta. Con C ++ 11 tenemos el nuevo y elegante bucle for:
for (const auto &s : schemas) names.push_back(s.first);
donde esquemas es un
std::map
y nombres es unstd::vector
.Esto llena la matriz (nombres) con claves del mapa (esquemas); cambie
s.first
as.second
para obtener una matriz de valores.fuente
const auto &s
reserve()
y obtendrá otra ganancia de rendimiento. ¡Con la llegada de C ++ 11, esa debería ser ahora la solución aceptada!Si está utilizando las bibliotecas boost , puede usar boost :: bind para acceder al segundo valor del par de la siguiente manera:
#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 solución se basa en una publicación de Michael Goldshteyn en la lista de correo de boost .
fuente
#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 no haber agregado ninguna explicación; pensé que el código es tan simple que no requiere ninguna explicación. Entonces:
transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
esta función llama
unaryOperation
a todos los elementos delinputIterator
rango (beginInputRange
-endInputRange
). El valor de la operación se almacena enoutputIterator
.Si queremos operar en todo el mapa, usamos map.begin () y map.end () como nuestro rango de entrada. Queremos almacenar nuestros valores de mapa en el vector - así que tenemos que utilizar back_inserter en nuestro vector:
back_inserter(your_values_vector)
. El back_inserter es un outputIterator especial que inserta nuevos elementos al final de la colección dada (como parámetro). El último parámetro es unaryOperation: solo toma un parámetro: el valor de inputIterator. Entonces podemos usar lambda :[](auto &kv) { [...] }
, donde & kv es solo una referencia al par de elementos del mapa. Entonces, si queremos devolver solo los valores de los elementos del mapa, simplemente podemos devolver kv.second:[](auto &kv) { return kv.second; }
Creo que esto explica cualquier duda.
fuente
Usando lambdas uno puede realizar lo siguiente:
{ 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); }); }
fuente
Esto es lo que haría.
También usaría una función de plantilla para facilitar la construcción de 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) ); }
fuente
Una forma es usar 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); }
fuente
Por qué no:
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 (cualquier mapa);
fuente
Pensé que debería ser
std::transform( map.begin(), map.end(), std::back_inserter(vec), boost::bind(&MapT::value_type::first,_1) );
fuente
Deberíamos usar la función de transformación del algoritmo STL, el último parámetro de la función de transformación podría ser un objeto de función, un puntero de función o una función lambda que convierte un elemento del mapa en un elemento del vector. Este mapa de casos tiene elementos que tienen un par de tipos que deben convertirse en elementos que tienen un tipo int para vector. Aquí está mi solución que uso la función 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; });
fuente
Sorprendido que nadie haya mencionado la solución más obvia , use el constructor 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()); }
fuente