2008-09-21 13 views
157

Ésta es una de las posibles formas que salen:¿Cómo recuperar todas las claves (o valores) de un std :: map y ponerlas en un vector?

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

Por supuesto, también podemos recuperar todos los valores del mapa mediante la definición de otro funtor RetrieveValues ​​.

¿Hay alguna otra manera de lograr esto fácilmente? (Siempre me pregunto por qué std :: map no incluye una función de miembro para que lo hagamos).

+3

su solución es la mejor ... – linello

Respuesta

124

Si bien su solución debería funcionar, puede ser difícil de leer dependiendo del nivel de habilidad de sus compañeros programadores. Además, aleja la funcionalidad del sitio de llamadas. Lo cual puede hacer que el mantenimiento sea un poco más difícil.

No estoy seguro de si su objetivo es obtener las claves en un vector o imprimirlas para cout así que estoy haciendo ambas cosas. Usted puede intentar algo como esto:

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 aún más simple, si está utilizando 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, me gusta la versión BOOST_FOREACH porque hay menos escribir y es muy explícito acerca de lo está haciendo.

+1

Vaya a las cifras que terminaría aquí después de mi búsqueda en Google. La tuya es la respuesta * Yo * prefiero :) – mpen

+4

@Jere - ¿Has trabajado con 'BOOST_FOREACH'? El código que propone aquí es totalmente incorrecto – Manuel

+0

Manuel es correcto, la sintaxis para BOOST_FOREACH iterar sobre un mapa es primero definir el tipo de elemento sin comas (es decir, typedef pair element_type;) luego iterar utilizando los elementos reales (es decir, BOOST_FOREACH (element_type & e, m) –

10

El SGI STL tiene una extensión llamada select1st. Lástima que no está en STL estándar.

6

Su solución está muy bien, pero se puede utilizar un repetidor para hacerlo:

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

Además, si usted tiene Boost, el uso transform_iterator para evitar hacer una copia temporal de las llaves.

9

Creo que BOOST_FOREACH presentado arriba es agradable y limpio, sin embargo, hay otra opción que también usa 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, no creo este enfoque es tan limpio como el enfoque BOOST_FOREACH en este caso, pero boost :: lambda puede ser muy limpio en otros casos.

-5

(siempre estoy preguntando por qué std :: mapa no incluye una función miembro para que lo hagamos.)

Debido a que no puede hacerlo mejor que se puede hacer . Si la implementación de un método no será superior a la implementación de una función gratuita, entonces, en general, no debe escribir un método; deberías escribir una función gratuita.

Tampoco está claro de inmediato por qué es útil de todos modos.

+6

Existen otras razones además de la eficiencia para que una biblioteca proporcione un método, como la funcionalidad de "pilas incluidas" y una API encapsulada y coherente. Aunque es cierto que ninguno de esos términos describe el STL particularmente bien :) Re.no está claro por qué es útil, ¿de verdad? Creo que es bastante obvio por qué enumerar las claves disponibles es algo útil que se puede hacer con un mapa/dict: depende de para qué lo estés usando. – andybuckley

+3

Con este razonamiento, no deberíamos tener 'empty()' porque puede implementarse como 'size() == 0'. – gd1

+1

Lo que dijo @ gd1. Si bien no debería haber mucha redundancia funcional en una clase, insistir en absolutamente cero no es una buena idea IMO, al menos hasta que C++ nos permita "bendecir" las funciones gratuitas en los métodos. – einpoklum

3

La mejor solución no SGI, no impulso STL es ampliar mapa :: iterador de esta manera:

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

y luego usarlos como tan:

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

Dejaré que el lector cree también el const_iterator y los iteradores inversos si/cuando sea necesario. – Marius

35

C++ 0x nos ha dado una excelente solución adicional,:

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

En mi opinión, no hay nada excelente al respecto. std :: vector claves; keys.reserve (m_Inputs.size()); for (auto keyValue: m_Inputs) {keys.push_back (keyValue.first); } Es mucho mejor que la transformación críptica. Incluso en términos de rendimiento. Éste es mejor. – Jagannath

+2

También puede reservar el tamaño de las llaves si desea un rendimiento comparable. usa la transformación si quieres evitar un bucle for. – DanDan

+3

solo desea agregar - puede usar [] (const auto & pair) –

49

Hay una boost range adaptor para este propósito:

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

Hay un adaptador de rango map_values ​​similar para extraer los valores.

+0

Solución muy limpia, compacta y elegante. Quiéralo ! –

+1

Desafortunadamente, parece que 'boost :: adaptadores' no está disponible hasta Boost 1.43. La versión estable actual de Debian (Squeeze) solo ofrece Boost 1.42 –

+2

. Es una pena. Boost 1.42 fue lanzado en febrero de 2010, más de 2.5 años antes de Squeeze. – Alastair

110
//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); 
+0

No hay una etiqueta C++ 0x en la pregunta. – Jagannath

+32

C++ 11 es el estándar _current_ de C++. C++ == C++ 11 –

+9

No fue en el momento en que se formuló la pregunta, ni en el momento en que se dio esta respuesta. –

2

Poco de un C++ 11 toma:

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

Aquí está una plantilla de función agradable usando C++ 11 magia, trabajando tanto para std :: mapa, 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; 
} 

Échale un vistazo aquí: http://ideone.com/lYBzpL

3

@ respuesta de DanDan, usando C++ 11 es:

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

y usando C++ 14 (como se indica por @ ivan.ukr) podemos reemplazar decltype(map_in)::value_type con auto.

Cuestiones relacionadas