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

Owen
fuente
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.

Jere.Jones
fuente
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);
Juan
fuente
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.

Alastair
fuente
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;});
DanDan
fuente
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.

James Hirschorn
fuente
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
12

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

Chris Jester-Young
fuente
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
}
Brian R. Bondy
fuente
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);
}
Madiyar
fuente
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.

paxos1977
fuente
7

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

Marcelo Cantos
fuente
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;
}
Parques oxidados
fuente
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

Clemens Sielaff
fuente
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];
Mario
fuente
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;
}
Deniz Babat
fuente
-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);
TarmoPikaro
fuente
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.

DrPizza
fuente
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