web-dev-qa-db-fra.com

Comment récupérer toutes les clés (ou valeurs) d'un std :: map et les mettre dans un vecteur?

C’est l’une des façons possibles pour moi de sortir:

struct RetrieveKey
{
    template <typename T>
    typename T::first_type operator()(T keyValuePair) const
    {
        return keyValuePair.first;
    }
};

map<int, int> m;
vector<int> keys;

// Retrieve all keys
transform(m.begin(), m.end(), back_inserter(keys), RetrieveKey());

// Dump all keys
copy(keys.begin(), keys.end(), ostream_iterator<int>(cout, "\n"));

Bien sûr, nous pouvons aussi récupérer toutes les valeurs de la carte en définissant un autre foncteur RetrieveValues.

Y a-t-il un autre moyen d'y parvenir facilement? (Je me demande toujours pourquoi std :: map n'inclut pas de fonction membre pour le faire.)

213
Owen

Bien que votre solution doive fonctionner, il peut être difficile à lire en fonction du niveau de compétence de vos collègues programmeurs. De plus, il éloigne les fonctionnalités du site d’appel. Ce qui peut rendre la maintenance un peu plus difficile.

Je ne suis pas sûr que votre objectif soit d'obtenir les clés dans un vecteur ou de les imprimer au cout afin que je fasse les deux. Vous pouvez essayer quelque chose comme ça:

map<int, int> m;
vector<int> v;
for(map<int,int>::iterator it = m.begin(); it != m.end(); ++it) {
  v.Push_back(it->first);
  cout << it->first << "\n";
}

Ou encore plus simple, si vous utilisez Boost:

map<int,int> m;
pair<int,int> me; // what a map<int, int> is made of
vector<int> v;
BOOST_FOREACH(me, m) {
  v.Push_back(me.first);
  cout << me.first << "\n";
}

Personnellement, j'aime bien la version de BOOST_FOREACH car il y a moins de frappe et c'est très explicite à propos de ce qu'il fait.

158
Jere.Jones
//c++0x too
std::map<int,int> mapints;
std::vector<int> vints;
vints.reserve(mapints.size());
for(auto const& imap: mapints)
    vints.Push_back(imap.first);
135
Juan

Il existe un adaptateur de plage amplifiée à cette fin:

vector<int> keys;
// Retrieve all keys
boost::copy(m | boost::adaptors::map_keys, std::back_inserter(keys));

Il existe un adaptateur de plage map_values ​​similaire pour extraire les valeurs.

58
Alastair

C++ 0x nous a donné une excellente solution supplémentaire:

std::vector<int> keys;

std::transform(
    m_Inputs.begin(),
    m_Inputs.end(),
    std::back_inserter(keys),
    [](const std::map<int,int>::value_type &pair){return pair.first;});
43
DanDan

La réponse de @DanDan, en utilisant C++ 11, est la suivante:

using namespace std;
vector<int> keys;

transform(begin(map_in), end(map_in), back_inserter(keys), 
            [](decltype(map_in)::value_type const& pair) {
    return pair.first;
}); 

et en utilisant C++ 14 (comme indiqué par @ ivan.ukr), nous pouvons remplacer decltype(map_in)::value_type par auto.

12
James Hirschorn

Le STL SGI a une extension appelée select1st . Dommage que ce ne soit pas en standard STL!

10
Chris Jester-Young

Je pense que le BOOST_FOREACH présenté ci-dessus est agréable et propre. Cependant, il existe une autre option utilisant BOOST.

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

std::map<int, int> m;
std::vector<int> keys;

using namespace boost::lambda;

transform(      m.begin(), 
                m.end(), 
                back_inserter(keys), 
                bind( &std::map<int,int>::value_type::first, _1 ) 
          );

copy( keys.begin(), keys.end(), std::ostream_iterator<int>(std::cout, "\n") );

Personnellement, je ne pense pas que cette approche soit aussi propre que l'approche BOOST_FOREACH dans ce cas, mais boost :: lambda peut être vraiment propre dans les autres cas.

9
paxos1977

Votre solution est correcte mais vous pouvez utiliser un itérateur pour le faire:

std::map<int, int> m;
m.insert(std::pair<int, int>(3, 4));
m.insert(std::pair<int, int>(5, 6));
for(std::map<int, int>::const_iterator it = m.begin(); it != m.end(); it++)
{
    int key = it->first;
    int value = it->second;
    //Do something
}
7
Brian R. Bondy

De plus, si vous avez Boost, utilisez transform_iterator pour éviter de créer une copie temporaire des clés.

7
Marcelo Cantos

Un peu de c ++ 11 prend:

std::map<uint32_t, uint32_t> items;
std::vector<uint32_t> itemKeys;
for (auto & kvp : items)
{
    itemKeys.emplace_back(kvp.first);
    std::cout << kvp.first << std::endl;
}
6
Rusty Parks

Vous pouvez utiliser le boost :: transform_iterator polyvalent. Transform_iterator vous permet de transformer les valeurs itérées, par exemple dans notre cas lorsque vous souhaitez traiter uniquement les clés, pas les valeurs. Voir http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/transform_iterator.html#example

5
amit

Voici un modèle de fonction Nice utilisant la magie C++ 11, fonctionnant à la fois pour std :: map, std :: unordered_map:

template<template <typename...> class MAP, class KEY, class VALUE>
std::vector<KEY>
keys(const MAP<KEY, VALUE>& map)
{
    std::vector<KEY> result;
    result.reserve(map.size());
    for(const auto& it : map){
        result.emplace_back(it.first);
    }
    return result;
}

Vérifiez-le ici: http://ideone.com/lYBzpL

4
Clemens Sielaff

La meilleure solution STL non-sgi, non-boost consiste à étendre map :: iterator comme ceci:

template<class map_type>
class key_iterator : public map_type::iterator
{
public:
    typedef typename map_type::iterator map_iterator;
    typedef typename map_iterator::value_type::first_type key_type;

    key_iterator(const map_iterator& other) : map_type::iterator(other) {} ;

    key_type& operator *()
    {
        return map_type::iterator::operator*().first;
    }
};

// helpers to create iterators easier:
template<class map_type>
key_iterator<map_type> key_begin(map_type& m)
{
    return key_iterator<map_type>(m.begin());
}
template<class map_type>
key_iterator<map_type> key_end(map_type& m)
{
    return key_iterator<map_type>(m.end());
}

et ensuite les utiliser comme ceci:

        map<string,int> test;
        test["one"] = 1;
        test["two"] = 2;

        vector<string> keys;

//      // method one
//      key_iterator<map<string,int> > kb(test.begin());
//      key_iterator<map<string,int> > ke(test.end());
//      keys.insert(keys.begin(), kb, ke);

//      // method two
//      keys.insert(keys.begin(),
//           key_iterator<map<string,int> >(test.begin()),
//           key_iterator<map<string,int> >(test.end()));

        // method three (with helpers)
        keys.insert(keys.begin(), key_begin(test), key_end(test));

        string one = keys[0];
4
Marius

Légèrement similaire à l'un des exemples ici, simplifié à partir de std::map perspective d'utilisation.

template<class KEY, class VALUE>
std::vector<KEY> getKeys(const std::map<KEY, VALUE>& map)
{
    std::vector<KEY> keys(map.size());
    for (const auto& it : map)
        keys.Push_back(it.first);
    return keys;
}

Utilisez comme ceci:

auto keys = getKeys(yourMap);
0
TarmoPikaro

Basé sur la solution @ rusty-parks, mais en c ++ 17:

std::map<uint32_t, uint32_t> items;
std::vector<uint32_t> itemKeys;
for (auto const& [key, std:ignore] : items) {
  itemKeys.emplace_back(key);
}
0
Madiyar