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

StackOverflow https://stackoverflow.com/questions/110157

  •  02-07-2019
  •  | 
  •  

Question

C’est l’un des moyens possibles dont je sors:

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 également récupérer toutes les valeurs de la carte en définissant un autre foncteur RetrieveValues ??.

Existe-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.)

Était-ce utile?

La solution

Même si votre solution devrait 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 sais pas si votre objectif est d’obtenir les clés dans un vecteur ou de les imprimer sur cout afin que je fasse les deux. Vous pouvez essayer quelque chose comme ceci:

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.

Autres conseils

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

Il y a adaptateur de plage boost à cet effet:

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.

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

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

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 .

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

Je pense que le BOOST_FOREACH présenté ci-dessus est agréable et propre. Cependant, il existe une autre option qui utilise également 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 celle de BOOST_FOREACH dans ce cas, mais boost :: lambda peut être vraiment propre dans les autres cas.

Votre solution est satisfaisante 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
}

De même, si vous disposez de Boost, utilisez transform_iterator pour éviter de créer une copie temporaire des clés.

Vous pouvez utiliser le module polyvalent boost :: transform_iterator. 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 # exemple

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

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

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

puis utilisez-les comme suit:

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

Voici un joli modèle de fonction 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;
}

Découvrez-le ici: http://ideone.com/lYBzpL

Un peu similaire à l'un des exemples ici, simplifié de la perspective d'utilisation de std :: map .

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

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

(Je me demande toujours pourquoi std :: map n'inclut pas de fonction membre pour le faire.)

Parce qu’il ne peut pas le faire mieux que vous ne pouvez le faire. Si l'implémentation d'une méthode ne sera pas supérieure à celle d'une fonction libre, alors vous ne devriez généralement pas écrire de méthode; vous devriez écrire une fonction libre.

La raison pour laquelle elle est utile n’a pas non plus été immédiatement comprise.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top