web-dev-qa-db-fra.com

Copier des valeurs de carte dans un vecteur en STL

Travailler mon chemin à travers efficace 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 par rapport à 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 façon la plus propre de procéder?

67
Gilad Naor

Vous ne pouvez pas facilement utiliser une plage ici car l'itérateur que vous obtenez à partir d'une carte fait référence à une paire std ::, où les itérateurs que vous utiliseriez pour insérer dans un vecteur font référence à 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 ce qui est évident:

#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 je devais 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 );
    }
}
51
anon

Vous pourriez probablement utiliser std::transform à cet effet. Je préférerais peut-être la version Neils cependant, en fonction de 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.

51
Skurmedel

Si vous utilisez 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 article de Michael Goldshteyn sur boost mailing list .

23
OK.

Ancienne question, nouvelle réponse. Avec C++ 11, nous avons la nouvelle boucle for:

for (const auto &s : schemas)
   names.Push_back(s.first);

où schémas est un std::map et noms est un std::vector.

Cela remplit le tableau (noms) avec les clés de la carte (schémas); changement s.first à s.second pour obtenir un tableau de valeurs.

21
Seth

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); });
}
18
Matthieu N.
#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. Alors:

transform( beginInputRange, endInputRange, outputIterator, unaryOperation)

cette fonction appelle unaryOperation sur chaque élément de la plage inputIterator (beginInputRange-endInputRange). La valeur de l'opération est stockée dans outputIterator.

Si nous voulons fonctionner sur toute la carte, nous utilisons map.begin () et map.end () comme plage d'entrée. Nous voulons stocker nos valeurs de carte dans le vecteur - nous devons donc 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 n'est qu'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 retourner kv.second:

[](auto &kv) { return kv.second; }

Je pense que cela explique tout doute.

13
Aragornx

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)
                  );
}
8
Martin York

Je pensais que ça devrait être

std::transform( map.begin(), map.end(), 
                   std::back_inserter(vec), 
                   boost::bind(&MapT::value_type::first,_1) ); 
2
OJMAN

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);

1
Jan Wilmans

Nous devrions utiliser la fonction de transformation de l'algorithme STL, le dernier paramètre de la fonction de transformation pourrait être un objet fonction, un pointeur de fonction ou une fonction lambda qui convertit un élément de la carte en un élément du vecteur. Cette carte de cas contient des éléments dont la paire de types doit être convertie 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;
       });
1
Loc Tran

Une façon consiste à 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);
}
1
aJ.