Come recuperare tutte le chiavi (o valori) da una std :: map e inserirle in un vettore?

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

  •  02-07-2019
  •  | 
  •  

Domanda

Questo è uno dei modi possibili in cui esco:

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

Naturalmente, possiamo anche recuperare tutti i valori dalla mappa definendo un altro funzione RetrieveValues ??.

C'è un altro modo per farlo facilmente? (Mi chiedo sempre perché std :: map non include una funzione membro per consentirci di farlo.)

È stato utile?

Soluzione

Mentre la tua soluzione dovrebbe funzionare, può essere difficile da leggere a seconda del livello di abilità dei tuoi colleghi programmatori. Inoltre, sposta la funzionalità lontano dal sito di chiamata. Il che può rendere la manutenzione un po 'più difficile.

Non sono sicuro che il tuo obiettivo sia mettere le chiavi in ??un vettore o stamparle per tagliarle, quindi sto facendo entrambe le cose. Puoi provare qualcosa del genere:

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

O ancora più semplice, se stai usando 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";
}

Personalmente, mi piace la versione di BOOST_FOREACH perché c'è meno digitazione ed è molto esplicito su ciò che sta facendo.

Altri suggerimenti

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

Esiste un boost range adapter a questo scopo:

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

Esiste un adattatore di intervallo map_values ??simile per l'estrazione dei valori.

C ++ 0x ci ha fornito un'ulteriore, eccellente soluzione:

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 risposta di @DanDan, usando C ++ 11 è:

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

e usando C ++ 14 (come notato da @ ivan.ukr) possiamo sostituire decltype (map_in) :: value_type con auto .

SGI STL ha un'estensione chiamata select1st . Peccato che non sia in STL standard!

Penso che BOOST_FOREACH presentato sopra sia bello e pulito, tuttavia, c'è anche un'altra opzione che utilizza anche 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") );

Personalmente, non penso che questo approccio sia pulito come l'approccio BOOST_FOREACH in questo caso, ma boost :: lambda può essere davvero pulito in altri casi.

La tua soluzione va bene, ma puoi usare un iteratore per farlo:

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
}

Inoltre, se si dispone di Boost, utilizzare transform_iterator per evitare di creare una copia temporanea delle chiavi.

Puoi usare il versatile boost :: transform_iterator. Transform_iterator ti consente di trasformare i valori iterati, ad esempio nel nostro caso quando vuoi trattare solo delle chiavi, non dei valori. Vedi http://www.boost.org/ doc / libs / 1_36_0 / librerie / iteratore / doc / transform_iterator.html # esempio

Bit di un c ++ 11:

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 migliore soluzione STL non-sgi, non boost è quella di estendere map :: iterator in questo modo:

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

e poi usali così:

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

Ecco un bel modello di funzione che usa C ++ 11 magic, che funziona sia con 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;
}

Dai un'occhiata qui: http://ideone.com/lYBzpL

Leggermente simile a uno degli esempi qui, semplificato dalla prospettiva di utilizzo 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;
}

Usa in questo modo:

auto keys = getKeys(yourMap);

Basato sulla soluzione @ rusty-parks, ma in 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);
}
  

(Mi chiedo sempre perché std :: map non include una funzione membro per consentirci di farlo.)

Perché non può farlo meglio di quanto tu possa fare. Se l'implementazione di un metodo non sarà superiore all'implementazione di una funzione libera, in generale non dovresti scrivere un metodo; dovresti scrivere una funzione gratuita.

Inoltre non è immediatamente chiaro perché sia ??utile comunque.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top