Je me fraye un chemin à travers Effective STL pour le moment. Le point 5 suggère qu'il est généralement préférable d'utiliser les fonctions de membre de plage à leurs homologues à élément unique. Je souhaite actuellement copier toutes les valeurs d'une carte (c'est-à-dire - je n'ai pas besoin des clés) dans un vecteur.
Quelle est la manière la plus propre de faire cela?
c++
stl
containers
Gilad Naor
la source
la source
Réponses:
Vous ne pouvez pas facilement utiliser une plage ici car l'itérateur que vous obtenez d'une carte fait référence à un std :: pair, où les itérateurs que vous utiliseriez pour insérer dans un vecteur se réfèrent à un objet du type stocké dans le vecteur, qui est (si vous jetez la clé) pas une paire.
Je ne pense vraiment pas que cela devienne beaucoup plus propre que l'évidence:
#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 je réécrirais probablement en tant que fonction de modèle si j'allais l'utiliser plus d'une fois. Quelque chose comme:
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 ); } }
la source
Vous pourriez probablement utiliser
std::transform
à cette fin. Je préférerais peut-être la version Neils, selon ce qui est plus lisible.Exemple par xtofl (voir commentaires):
#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) ); }
Très générique, pensez à lui donner du crédit si vous le trouvez utile.
la source
Ancienne question, nouvelle réponse. Avec C ++ 11, nous avons la nouvelle fantaisie pour la boucle:
for (const auto &s : schemas) names.push_back(s.first);
où schemas est un
std::map
et names est unstd::vector
.Cela remplit le tableau (noms) avec les clés de la carte (schémas); changez
s.first
ens.second
pour obtenir un tableau de valeurs.la source
const auto &s
reserve()
et vous obtiendrez un autre gain de performances. Avec l'avènement du C ++ 11, cela devrait maintenant être la solution acceptée!Si vous utilisez les bibliothèques boost , vous pouvez utiliser boost :: bind pour accéder à la deuxième valeur de la paire comme suit:
#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) ); }
Cette solution est basée sur un message de Michael Goldshteyn sur la liste de diffusion boost .
la source
#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;} );
Désolé de ne pas avoir ajouté d'explication - je pensais que le code est si simple qu'il ne nécessite aucune explication. Donc:
transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
cette fonction appelle
unaryOperation
chaque élément de lainputIterator
plage (beginInputRange
-endInputRange
). La valeur de l'opération est stockée dansoutputIterator
.Si nous voulons opérer sur toute la carte, nous utilisons map.begin () et map.end () comme plage d'entrée. Nous voulons conserver nos valeurs dans le vecteur carte - donc nous devons utiliser back_inserter sur notre vecteur:
back_inserter(your_values_vector)
. Le back_inserter est un outputIterator spécial qui pousse de nouveaux éléments à la fin d'une collection donnée (en tant que paramètre). Le dernier paramètre est unaryOperation - il ne prend qu'un seul paramètre - la valeur de inputIterator. Nous pouvons donc utiliser lambda:,[](auto &kv) { [...] }
où & kv est juste une référence à la paire d'éléments de la carte. Donc, si nous voulons renvoyer uniquement les valeurs des éléments de la carte, nous pouvons simplement renvoyer kv.second:[](auto &kv) { return kv.second; }
Je pense que cela explique les doutes.
la source
En utilisant lambdas, on peut effectuer les opérations suivantes:
{ 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); }); }
la source
Voici ce que je ferais.
J'utiliserais également une fonction de modèle pour faciliter la construction 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) ); }
la source
Une façon est d'utiliser 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); }
la source
Pourquoi pas:
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; }
usage:
auto vec = MapValuesAsVector (anymap);
la source
J'ai pensé que ça devrait être
std::transform( map.begin(), map.end(), std::back_inserter(vec), boost::bind(&MapT::value_type::first,_1) );
la source
Nous devrions utiliser la fonction de transformation de l'algorithme STL, le dernier paramètre de la fonction de transformation pourrait être un objet de fonction, un pointeur de fonction ou une fonction lambda qui convertit l'élément de la carte en élément du vecteur. Cette carte de cas contient des éléments ayant une paire de types qui doivent être convertis en élément de type int pour vecteur. Voici ma solution que j'utilise la fonction 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; });
la source
Surpris que personne n'ait mentionné la solution la plus évidente , utilisez le constructeur 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()); }
la source