¿Hay alguna forma de iterar sobre las teclas, no los pares de un mapa de C ++?
¿Hay alguna forma de iterar sobre las teclas, no los pares de un mapa de C ++?
Respuestas:
Si realmente necesita ocultar el valor que devuelve el iterador "real" (por ejemplo, porque desea utilizar su iterador de clave con algoritmos estándar, para que operen en las claves en lugar de los pares), eche un vistazo a Boost transform_iterator .
[Consejo: cuando mire la documentación de Boost para una nueva clase, lea primero los "ejemplos" al final. Entonces tienes una oportunidad deportiva de descubrir de qué demonios está hablando el resto :-)]
El mapa es un contenedor asociativo. Por lo tanto, el iterador es un par de claves, 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 desee exponer solo las teclas al exterior, puede convertir el mapa a vector o teclas y exponer.
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.
Con C ++ 11, la sintaxis de iteración es simple. Todavía itera sobre pares, pero acceder solo a la clave es fácil.
#include <iostream>
#include <map>
int 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";
}
}
Puede hacerlo simplemente extendiendo el iterador STL para ese mapa. Por ejemplo, una asignación 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.
Utiliza su iterador exactamente como lo haría con un iterador de lista, excepto que está iterando sobre el mapa begin()
y end()
.
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());
template<typename C> class key_iterator : public C::iterator
, etc.
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';
}
}
Desafortunadamente, el estándar C ++ 17 requiere que declare la value
variable, aunque no la esté usando ( std::ignore
como se usaría para std::tie(..)
no funciona, vea esta discusión ).
¡Por lo tanto, algunos compiladores pueden advertirle sobre la value
variable no utilizada ! Las advertencias en tiempo de compilación con respecto a las variables no utilizadas son una prohibición para cualquier código de producción en mi mente. Por lo tanto, esto puede no ser aplicable para ciertas versiones del compilador.
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Debajo de la solución más general a la que se refirió Ian ...
#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.
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;
}
}
Cuando no es explícito begin
y end
es necesario, es decir, para el bucle de rango, el bucle sobre las teclas (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;
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;
.....
}
Si necesita un iterador que solo devuelva las claves, debe ajustar el iterador del mapa en su propia clase que proporcione la interfaz deseada. Puede declarar una nueva clase de iterador desde cero como aquí , para usar construcciones auxiliares existentes. Esta respuesta muestra cómo usar Boost transform_iterator
para ajustar el iterador en uno que solo devuelve los valores / claves.
Esta respuesta es como la de rodrigob, excepto sin el BOOST_FOREACH
. Puede usar el rango de c ++ basado 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;
}
}
Sin Boost, podrías hacerlo así. Sería bueno si pudiera escribir un operador de conversión en lugar de getKeyIterator (), pero no puedo hacer que se 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());
}
}
Para la posteridad, y dado que estaba tratando de encontrar una manera de crear un rango, una alternativa es usar boost :: adapters :: 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, utilícelo t.second
en lambda.
Muchas buenas respuestas aquí, a continuación hay un enfoque que utiliza 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 quiso, entonces 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);
}
Adopté la respuesta de Ian para trabajar con todos los tipos de mapas y arreglé la devolución de una referencia para operator*
template<typename T>
class MapKeyIterator : public T
{
public:
MapKeyIterator() : T() {}
MapKeyIterator(T iter) : T(iter) {}
auto* operator->()
{
return &(T::operator->()->first);
}
auto& operator*()
{
return T::operator*().first;
}
};
Sé que esto no responde a su pregunta, pero una opción que puede considerar es simplemente 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 bucle rápido for vName.size (), y cuando lo encuentre, ese es el índice de vNameCount que está buscando.
Claro que esto puede no darle toda la funcionalidad del mapa, y dependiendo puede o no ser mejor, pero podría ser más fácil si no conoce las claves y no debería agregar demasiado procesamiento.
Solo recuerde que cuando agregue / elimine uno, debe hacerlo desde el otro o las cosas se volverán locas je: P