¿Cómo recuperar todas las claves (o valores) de un std :: map y ponerlas en un vector?


246

Esta es una de las posibles formas en que salgo:

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 al definir otro functor RetrieveValues .

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


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

44
Lo único que agregaría es esto keys.reserve(m.size());.
Galik

Respuestas:


176

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 la llamada. Lo que puede dificultar un poco el mantenimiento.

No estoy seguro de si su objetivo es obtener las claves en un vector o imprimirlas en Cout, así que estoy haciendo ambas. Puedes probar 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 incluso más simple, si estás 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, me gusta la versión BOOST_FOREACH porque se escribe menos y es muy explícita sobre lo que está haciendo.


1
Vaya, creo que terminaría aquí después de mi búsqueda en Google. La tuya es la respuesta que prefiero :)
MPes

44
@Jere - ¿De verdad has trabajado BOOST_FOREACH? El código que propone aquí es totalmente incorrecto
Manuel

2
@Jamie: esa es otra forma, pero los documentos de impulso muestran la especificación de la variable y su tipo antes de BOOST_FOREACH si el tipo contiene una coma. También muestran typedefing it. Entonces, estoy confundido, ¿qué tiene de malo mi código?
Jere.Jones

17
Curioso, ¿no tendría sentido presize el vector para evitar la asignación de cambio de tamaño?
Alan

2
No olvide hacer v.reserve(m.size())para evitar que el vector cambie de tamaño durante la transferencia.
Brian White

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

44
Agradable. Olvídate de it = ...begin(); it != ...end. Lo mejor sería, por supuesto, std :: map con un método de teclas () que devuelve ese vector ...
masterxilo

2
@BenHymers: Me parece que esta respuesta se dio en answered Mar 13 '12 at 22:33, que es varios meses después de que C ++ 11 se convirtió en C ++.
Sebastian Mach

37
@BenHymers, pero es útil para cualquiera que lea la pregunta ahora, que es de lo que se trata SO, no solo ayudar al que pregunta, sino a todos los demás.
Luchian Grigore

99
for (auto & imap) es más preciso porque no hay operación de copia.
HelloWorld

2
@StudentT, mejor aún, for(auto const & imap : mapints).
cp.engr

61

Hay un adaptador de rango de refuerzo 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.


1
Desafortunadamente, parece que boost::adaptorsno está disponible hasta Boost 1.43. La versión estable actual de Debian (Squeeze) solo ofrece Boost 1.42
Mickaël Le Baillif

2
Eso es una lástima. Boost 1.42 se lanzó en febrero de 2010, más de 2.5 años antes de Squeeze.
Alastair

En este punto, ¿no deberían Squeeze Updates y / o el repositorio de backports ofrecer Boost 1.44?
Luis Machuca

¿en qué encabezado de impulso está definido?
James Wierzba

1
Vea el documento vinculado, se define enboost/range/adaptor/map.hpp
Alastair

46

C ++ 0x nos ha dado otra solución excelente:

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

22
En mi opinión, no hay nada excelente al respecto. teclas std :: vector <int>; 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

55
Puede reservar el tamaño de las teclas aquí también si desea un rendimiento comparable. use la transformación si desea evitar un bucle for.
DanDan

44
solo quiero agregar - puede usar [] (const auto & pair)
ivan.ukr

@ ivan.ukr ¿qué compilador estás usando? Esta sintaxis no está permitida aquí: 'const auto &': un parámetro no puede tener un tipo que contenga 'auto'
Gobe

44
@ ivan.ukr parámetro automático en lambda es c ++ 14
roalz

16

La 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 lo señaló @ ivan.ukr) podemos reemplazarlo decltype(map_in)::value_typepor auto.


55
Podría agregar keys.reserve(map_in.size());para la eficiencia.
Galik

Creo que el método de transformación en realidad toma más código que for-loop.
user1633272

Const se puede poner detrás del tipo! Casi me olvido de eso.
Zhang


10

Su solución está bien, pero puede usar un iterador 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
}

10

Basado en la solución @ rusty-parks, pero en c ++ 17:

std :: map <int, int> elementos;
std :: vector <int> itemKeys;

for (const auto & [key, ignored]: items)
{
    itemKeys.push_back (clave);
}

No creo que se std::ignorepueda usar en enlaces estructurados de esta manera. Recibo un error de compilación. Debería ser suficiente usar una variable regular, por ejemplo, ignoredque simplemente no se usa.
jb

1
@jb Gracias. De hecho, std::ignoreestá destinado para su uso con, std::tiepero no con enlaces estructurales. He actualizado mi código.
Madiyar

9

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


7

Además, si tiene Boost, use transform_iterator para evitar hacer una copia temporal de las claves.


7

Un poco de una toma de 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;
}


5

Aquí hay una buena plantilla de función que usa magia C ++ 11, que funciona tanto para 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;
}

Compruébalo aquí: http://ideone.com/lYBzpL


4

La mejor solución STL no sgi y no boost es extender map :: iterator 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 úsalos así:

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

1
Dejaré que el lector también cree el const_iterator y los iteradores inversos si es necesario.
Marius

-1

Con ejemplo de mapa atómico

#include <iostream>
#include <map>
#include <vector> 
#include <atomic>

using namespace std;

typedef std::atomic<std::uint32_t> atomic_uint32_t;
typedef std::map<int, atomic_uint32_t> atomic_map_t;

int main()
{
    atomic_map_t m;

    m[4] = 456;
    m[2] = 45678;

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

    return 0;
}

-2

Ligeramente similar a uno de los ejemplos aquí, simplificado desde la std::mapperspectiva de uso.

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

Usar así:

auto keys = getKeys(yourMap);

2
Oye, sé que esta respuesta es antigua, pero también está mal. Inicializar con tamaño map.size()significa duplicar el retorno del tamaño del vector. Por favor, arregle para salvar a alguien más el dolor de cabeza :(
jue

-3

(Siempre me pregunto por qué std :: map no incluye una función miembro para que lo hagamos).

Porque no puede hacerlo mejor de lo que tú puedes hacerlo. Si la implementación de un método no será superior a la implementación de una función libre, en general no debe escribir un método; Deberías escribir una función libre.

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


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

44
Según este razonamiento, no deberíamos hacerlo empty()porque se puede implementar 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 libres en métodos.
einpoklum

1
En versiones anteriores de C ++ había contenedores para los que empty () y size () podían razonablemente tener diferentes garantías de rendimiento, y creo que la especificación era lo suficientemente flexible como para permitir esto (específicamente, listas vinculadas que ofrecían empalme de tiempo constante ()) . Como tal, desacoplarlos tenía sentido. Sin embargo, no creo que esta discrepancia se permita más.
DrPizza

Estoy de acuerdo. C ++ trata std::map<T,U>como un contenedor de pares. En Python, un dictactúa como sus teclas cuando se repite, pero le permite decir d.items()que obtenga el comportamiento de C ++. Python también proporciona d.values(). std::map<T,U>sin duda podría proporcionar una keys()y values()método que devuelve un objeto que tiene begin()y end()que proporcionan iteradores sobre las claves y valores.
Ben
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.