Iterar claves en un mapa C ++

¿Hay alguna manera de iterar sobre las claves, no los pares de un mapa C ++?

Si realmente necesita ocultar el valor que devuelve el iterador “real” (por ejemplo, porque quiere usar su iterador de claves con algoritmos estándar, para que operen en las teclas en lugar de los pares), eche un vistazo a Boost’s transform_iterator .

[Sugerencia: al consultar la documentación de Boost para una nueva clase, primero lea los “ejemplos” al final. Entonces tienes una posibilidad deportiva de averiguar de qué demonios habla el rest :-)]

el mapa es un contenedor asociativo. Por lo tanto, el iterador es un par de clave, val. SI solo necesita claves, puede ignorar la parte del valor del par.

for(std::map::iterator iter = myMap.begin(); iter != myMap.end(); ++iter) { Key k = iter->first; //ignore value //Value v = iter->second; } 

EDITAR:: en caso de que quiera exponer solo las claves al exterior, puede convertir el mapa en vectores o claves y exponer.

Con C ++ 11, la syntax de iteración es simple. Todavía iteras sobre pares, pero acceder solo a la clave es fácil.

 #include  #include  main() { std::map myMap; myMap["one"] = 1; myMap["two"] = 2; myMap["three"] = 3; for ( const auto &myPair : myMap ) { std::cout << myPair.first << "\n"; } } 

Sin Boost

Puede hacer esto simplemente extendiendo el iterador STL para ese mapa. Por ejemplo, un mapeo de cadenas a ints:

 #include  typedef map ScoreMap; typedef ScoreMap::iterator ScoreMapIterator; class key_iterator : public ScoreMapIterator { public: key_iterator() : ScoreMapIterator() {}; key_iterator(ScoreMapIterator s) : ScoreMapIterator(s) {}; string* operator->() { return (string* const)&(ScoreMapIterator::operator->()->first); } string operator*() { return ScoreMapIterator::operator*().first; } }; 

También puede realizar esta extensión en una plantilla , para una solución más general.

Usas tu iterador exactamente como si usaras un iterador de lista, excepto que estás iterando sobre el begin() y el end() del mapa.

 ScoreMap m; m["jim"] = 1000; m["sally"] = 2000; for (key_iterator s = m.begin(); s != m.end(); ++s) printf("\n key %s", s->c_str()); 

Está buscando map_keys , con él puede escribir cosas como

 BOOST_FOREACH(const key_t key, the_map | boost::adaptors::map_keys) { // do something with key } 

Debajo de la solución de plantilla más general a la que Ian se refirió …

 #include  template using Map = std::map; template using MapIterator = typename Map::iterator; template class MapKeyIterator : public MapIterator { public: MapKeyIterator ( ) : MapIterator ( ) { }; MapKeyIterator ( MapIterator it_ ) : MapIterator ( it_ ) { }; Key *operator -> ( ) { return ( Key * const ) &( MapIterator::operator -> ( )->first ); } Key operator * ( ) { return MapIterator::operator * ( ).first; } }; template class MapValueIterator : public MapIterator { public: MapValueIterator ( ) : MapIterator ( ) { }; MapValueIterator ( MapIterator it_ ) : MapIterator ( it_ ) { }; Value *operator -> ( ) { return ( Value * const ) &( MapIterator::operator -> ( )->second ); } Value operator * ( ) { return MapIterator::operator * ( ).second; } }; 

Todos los créditos van a Ian … Gracias Ian.

Con C ++ 17 puede usar un enlace estructurado dentro de un bucle for basado en rango (adaptando la respuesta de John H. en consecuencia):

 #include  #include  int main() { std::map myMap; myMap["one"] = 1; myMap["two"] = 2; myMap["three"] = 3; for ( const auto &[key, value]: myMap ) { std::cout << key << '\n'; } } 

Lamentablemente, el estándar C ++ 17 requiere que declare la variable de value , aunque no la esté usando ( std::ignore como se usaría para std::tie(..) no funciona, consulte esta explicación ). Por lo tanto, su comstackdor le advertirá sobre la variable de value no utilizada.

Las advertencias de tiempo de comstackción con respecto a las variables no utilizadas son un obstáculo para cualquier código de producción en mi mente. Entonces este es solo un ejemplo hipotético de integridad.

Aquí hay un ejemplo de cómo hacerlo usando el transform_iterator de Boost

 #include  #include  #include  #include "boost/iterator/transform_iterator.hpp" using std::map; typedef std::string Key; typedef std::string Val; map::key_type get_key(map::value_type aPair) { return aPair.first; } typedef map::key_type (*get_key_t)(map::value_type); typedef map::iterator map_iterator; typedef boost::transform_iterator mapkey_iterator; int main() { map m; m["a"]="A"; m["b"]="B"; m["c"]="C"; // iterate over the map's (key,val) pairs as usual for(map_iterator i = m.begin(); i != m.end(); i++) { std::cout << i->first << " " << i->second << std::endl; } // iterate over the keys using the transformed iterators mapkey_iterator keybegin(m.begin(), get_key); mapkey_iterator keyend(m.end(), get_key); for(mapkey_iterator i = keybegin; i != keyend; i++) { std::cout << *i << std::endl; } } 

¿Quieres hacer esto?

 std::map::iterator iter = myMap.begin(); std::map::iterator iter = myMap.end(); for(; iter != endIter; ++iter) { type key = iter->first; ..... } 

Si necesita un iterador que simplemente devuelva las claves, debe ajustar el iterador del mapa en su propia clase que proporciona la interfaz deseada. Puede declarar una nueva clase de iterador desde cero como aquí , de usar construcciones de ayudantes existentes. Esta respuesta muestra cómo usar el transform_iterator de Boost para envolver el iterador en uno que solo devuelve los valores / claves.

Cuando no se necesita un begin y un end explícitos, es decir, para el bucle de rango, las teclas de bucle (primer ejemplo) o los valores (segundo ejemplo) se pueden obtener con

 #include  map m; for (auto k : boost::adaptors::keys(m)) cout << k << endl; for (auto v : boost::adaptors::values(m)) cout << v << endl; 

Tú podrías

  • crear una clase de iterador personalizada, agregando el std::map::iterator
  • use std::transform de su map.begin() para map.end() con un boost::bind( &pair::second, _1 ) functor
  • simplemente ignore el ->second miembro mientras itera con un ciclo for .

Esta respuesta es como la de rodrigob, excepto sin el BOOST_FOREACH . Puede usar el rango de c ++ en su lugar.

 #include  #include  #include  template  void printKeys(std::map map){ for(auto key : map | boost::adaptors::map_keys){ std::cout << key << std::endl; } } 

Sé que esto no responde a su pregunta, pero una opción que quizás quiera considerar es tener dos vectores con el mismo índice como información “vinculada”.

Entonces en ..

 std::vector vName; std::vector vNameCount; 

si desea el recuento de nombres por nombre, simplemente haga su ciclo rápido por vName.size (), y cuando lo encuentre, ese es el índice de vNameCount que está buscando.

Claro que esto puede no proporcionarle toda la funcionalidad del mapa, y depender puede o no ser mejor, pero podría ser más fácil si no conoce las claves y no debe agregar demasiado procesamiento.

Solo recuerda que cuando agregas / borras de una tienes que hacerlo desde la otra o las cosas se vuelven locas je: P

Sin Boost, podrías hacerlo así. Sería bueno si pudieras escribir un operador de transmisión en lugar de getKeyIterator (), pero no puedo hacer que compile.

 #include  #include  template class key_iterator: public std::unordered_map::iterator { public: const K &operator*() const { return std::unordered_map::iterator::operator*().first; } const K *operator->() const { return &(**this); } }; template key_iterator getKeyIterator(typename std::unordered_map::iterator &it) { return *static_cast *>(&it); } int _tmain(int argc, _TCHAR* argv[]) { std::unordered_map myMap; myMap["one"]="A"; myMap["two"]="B"; myMap["three"]="C"; key_iterator &it=getKeyIterator(myMap.begin()); for (; it!=myMap.end(); ++it) { printf("%s\n",it->c_str()); } } 

Para la posteridad, y como estaba tratando de encontrar una manera de crear un rango, una alternativa es usar boost :: adapters :: transform

Aquí hay un pequeño ejemplo:

 #include  #include  #include  int main(int argc, const char* argv[]) { std::map m; m[0] = 1; m[2] = 3; m[42] = 0; auto key_range = boost::adaptors::transform( m, [](std::map::value_type const& t) { return t.first; } ); for (auto&& key : key_range) std::cout << key << ' '; std::cout << '\n'; return 0; } 

Si desea iterar sobre los valores, use t.second en el lambda.

Muchas buenas respuestas aquí, a continuación se muestra un enfoque que usa un par de ellas que le permite escribir esto:

 void main() { std::map m { {"jim", 1000}, {"sally", 2000} }; for (auto key : MapKeys(m)) std::cout << key << std::endl; } 

Si eso es lo que siempre quisiste, aquí está el código para MapKeys ():

 template  class MapKeyIterator { public: class iterator { public: iterator(typename MapType::iterator it) : it(it) {} iterator operator++() { return ++it; } bool operator!=(const iterator & other) { return it != other.it; } typename MapType::key_type operator*() const { return it->first; } // Return key part of map private: typename MapType::iterator it; }; private: MapType& map; public: MapKeyIterator(MapType& m) : map(m) {} iterator begin() { return iterator(map.begin()); } iterator end() { return iterator(map.end()); } }; template  MapKeyIterator MapKeys(MapType& m) { return MapKeyIterator(m); }