pasos - Iterar claves en un mapa C++
que es un mapa conceptual (17)
Sin Boost
Puede hacer esto simplemente extendiendo el iterador STL para ese mapa. Por ejemplo, un mapeo de cadenas a ints:
#include <map>
typedef map<string, int> 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());
¿Hay alguna manera de iterar sobre las claves, no los pares de un mapa C ++?
¿Quieres hacer esto?
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();
for(; iter != endIter; ++iter)
{
type key = iter->first;
.....
}
Aquí hay un ejemplo de cómo hacerlo usando el transform_iterator de Boost
#include <iostream>
#include <map>
#include <iterator>
#include "boost/iterator/transform_iterator.hpp"
using std::map;
typedef std::string Key;
typedef std::string Val;
map<Key,Val>::key_type get_key(map<Key,Val>::value_type aPair) {
return aPair.first;
}
typedef map<Key,Val>::key_type (*get_key_t)(map<Key,Val>::value_type);
typedef map<Key,Val>::iterator map_iterator;
typedef boost::transform_iterator<get_key_t, map_iterator> mapkey_iterator;
int main() {
map<Key,Val> 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;
}
}
Con C ++ 11, la sintaxis de iteración es simple. Todavía iteras sobre pares, pero acceder solo a la clave es fácil.
#include <iostream>
#include <map>
main()
{
std::map<std::string, int> myMap;
myMap["one"] = 1;
myMap["two"] = 2;
myMap["three"] = 3;
for ( const auto &myPair : myMap ) {
std::cout << myPair.first << "/n";
}
}
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 <iostream>
#include <map>
int main() {
std::map<std::string, int> 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 compilador le advertirá sobre la variable de value
no utilizada.
Las advertencias de tiempo de compilació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.
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 <boost/range/adaptors.hpp>
map<Key, Value> m;
for (auto k : boost::adaptors::keys(m))
cout << k << endl;
for (auto v : boost::adaptors::values(m))
cout << v << endl;
Debajo de la solución de plantilla más general a la que Ian se refirió ...
#include <map>
template<typename Key, typename Value>
using Map = std::map<Key, Value>;
template<typename Key, typename Value>
using MapIterator = typename Map<Key, Value>::iterator;
template<typename Key, typename Value>
class MapKeyIterator : public MapIterator<Key, Value> {
public:
MapKeyIterator ( ) : MapIterator<Key, Value> ( ) { };
MapKeyIterator ( MapIterator<Key, Value> it_ ) : MapIterator<Key, Value> ( it_ ) { };
Key *operator -> ( ) { return ( Key * const ) &( MapIterator<Key, Value>::operator -> ( )->first ); }
Key operator * ( ) { return MapIterator<Key, Value>::operator * ( ).first; }
};
template<typename Key, typename Value>
class MapValueIterator : public MapIterator<Key, Value> {
public:
MapValueIterator ( ) : MapIterator<Key, Value> ( ) { };
MapValueIterator ( MapIterator<Key, Value> it_ ) : MapIterator<Key, Value> ( it_ ) { };
Value *operator -> ( ) { return ( Value * const ) &( MapIterator<Key, Value>::operator -> ( )->second ); }
Value operator * ( ) { return MapIterator<Key, Value>::operator * ( ).second; }
};
Todos los créditos van a Ian ... Gracias Ian.
Está buscando boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/… , con él puede escribir cosas como
BOOST_FOREACH(const key_t key, the_map | boost::adaptors::map_keys)
{
// do something with key
}
Esta respuesta es como la de rodrigob, excepto sin el BOOST_FOREACH
. Puede usar el rango de c ++ en su lugar.
#include <map>
#include <boost/range/adaptor/map.hpp>
#include <iostream>
template <typename K, typename V>
void printKeys(std::map<K,V> map){
for(auto key : map | boost::adaptors::map_keys){
std::cout << key << std::endl;
}
}
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<std::string, int> 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 MapType>
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 <class MapType>
MapKeyIterator<MapType> MapKeys(MapType& m)
{
return MapKeyIterator<MapType>(m);
}
Para la posteridad, y como estaba tratando de encontrar una manera de crear un rango, una alternativa es usar boost::adaptors::transform
Aquí hay un pequeño ejemplo:
#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <map>
int main(int argc, const char* argv[])
{
std::map<int, int> m;
m[0] = 1;
m[2] = 3;
m[42] = 0;
auto key_range =
boost::adaptors::transform(
m,
[](std::map<int, int>::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.
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<std::string> vName;
std::vector<int> 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
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 here , 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.
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 resto :-)]
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 <map>
#include <unordered_map>
template<typename K, typename V>
class key_iterator: public std::unordered_map<K,V>::iterator {
public:
const K &operator*() const {
return std::unordered_map<K,V>::iterator::operator*().first;
}
const K *operator->() const {
return &(**this);
}
};
template<typename K,typename V>
key_iterator<K,V> getKeyIterator(typename std::unordered_map<K,V>::iterator &it) {
return *static_cast<key_iterator<K,V> *>(&it);
}
int _tmain(int argc, _TCHAR* argv[])
{
std::unordered_map<std::string, std::string> myMap;
myMap["one"]="A";
myMap["two"]="B";
myMap["three"]="C";
key_iterator<std::string, std::string> &it=getKeyIterator<std::string,std::string>(myMap.begin());
for (; it!=myMap.end(); ++it) {
printf("%s/n",it->c_str());
}
}
Tú podrías
- crear una clase de iterador personalizada, agregando el
std::map<K,V>::iterator
- use
std::transform
de sumap.begin()
paramap.end()
con unboost::bind( &pair::second, _1 )
functor - simplemente ignore el
->second
miembro mientras itera con un ciclofor
.
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<Key,Val>::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.