C'è un modo per scorrere le chiavi, non le coppie di una mappa C ++?
C'è un modo per scorrere le chiavi, non le coppie di una mappa C ++?
Risposte:
Se hai davvero bisogno di nascondere il valore restituito dall'iteratore "reale" (ad esempio perché vuoi usare il tuo iteratore di chiavi con algoritmi standard, in modo che agiscano sui tasti invece che sulle coppie), dai un'occhiata a Boost's transform_iterator .
[Suggerimento: quando guardi la documentazione di Boost per una nuova classe, leggi prima gli "esempi" alla fine. Hai quindi la possibilità sportiva di capire di cosa diavolo sta parlando il resto :-)]
la mappa è un contenitore associativo. Quindi, l'iteratore è una coppia di chiavi, val. SE hai bisogno solo delle chiavi, puoi ignorare la parte del valore dalla coppia.
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k = iter->first;
//ignore value
//Value v = iter->second;
}
EDIT:: Nel caso in cui desideri esporre solo le chiavi all'esterno, puoi convertire la mappa in vettoriale o chiavi ed esporre.
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.
Con C ++ 11 la sintassi dell'iterazione è semplice. Continui a iterare sulle coppie, ma accedere solo alla chiave è facile.
#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";
}
}
Puoi farlo semplicemente estendendo l'iteratore STL per quella mappa. Ad esempio, una mappatura di stringhe su int:
#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; }
};
Puoi anche eseguire questa estensione in un modello , per una soluzione più generale.
Usi il tuo iteratore esattamente come useresti un iteratore di lista, tranne per il fatto che stai iterando sulla mappa begin()
e 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
, ecc
Con C ++ 17 puoi usare un'associazione strutturata all'interno di un ciclo for basato su intervalli (adattando di conseguenza la risposta di John H. ):
#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';
}
}
Sfortunatamente lo standard C ++ 17 richiede di dichiarare il value
variabile, anche se non la stai usando ( std::ignore
dato che uno userebbe per std::tie(..)
non funziona, vedi questa discussione ).
Alcuni compilatori potrebbero quindi avvisarti del mancato utilizzo value
variabile ! Gli avvisi in fase di compilazione riguardanti le variabili inutilizzate sono un no-go per qualsiasi codice di produzione nella mia mente. Quindi, questo potrebbe non essere applicabile per alcune versioni del compilatore.
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Di seguito la soluzione modellata più generale a cui Ian si riferiva ...
#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; }
};
Tutti i riconoscimenti vanno a Ian ... Grazie Ian.
Ecco un esempio di come farlo usando il transform_iterator di 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;
}
}
Quando non è esplicito begin
ed end
è necessario, cioè per il range-loop, il loop su chiavi (primo esempio) o valori (secondo esempio) può essere ottenuto 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;
Lo vuoi fare?
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();
for(; iter != endIter; ++iter)
{
type key = iter->first;
.....
}
Se hai bisogno di un iteratore che restituisca solo le chiavi, devi avvolgere l'iteratore della mappa nella tua classe che fornisce l'interfaccia desiderata. Puoi dichiarare una nuova classe iteratore da zero come qui , o utilizzare i costrutti di supporto esistenti. Questa risposta mostra come utilizzare Boost transform_iterator
per racchiudere l'iteratore in uno che restituisca solo i valori / chiavi.
Questa risposta è come quella di Rodrigob tranne che senza BOOST_FOREACH
. Puoi invece usare l'intervallo di c ++ basato per.
#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;
}
}
Senza Boost, potresti farlo in questo modo. Sarebbe bello se potessi scrivere un operatore cast invece di getKeyIterator (), ma non riesco a farlo compilare.
#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());
}
}
Per i posteri, e poiché stavo cercando di trovare un modo per creare una gamma, un'alternativa è usare boost :: adapters :: transform
Ecco un piccolo esempio:
#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;
}
Se vuoi iterare sui valori, usa t.second
nel lambda.
Molte buone risposte qui, di seguito è riportato un approccio che ne utilizza un paio che ti consente di scrivere questo:
void main()
{
std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
for (auto key : MapKeys(m))
std::cout << key << std::endl;
}
Se è quello che hai sempre desiderato, ecco il codice per 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);
}
Ho adottato la risposta di Ian per lavorare con tutti i tipi di mappa e ho corretto la restituzione di un riferimento per 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;
}
};
So che questo non risponde alla tua domanda, ma un'opzione che potresti voler esaminare è avere solo due vettori con lo stesso indice che sono informazioni "collegate" ..
Quindi in ..
std::vector<std::string> vName;
std::vector<int> vNameCount;
se vuoi il conteggio dei nomi per nome, fai semplicemente il tuo ciclo rapido su vName.size (), e quando lo trovi quello è l'indice per vNameCount che stai cercando.
Sicuramente questo potrebbe non darti tutte le funzionalità della mappa e, a seconda, potrebbe essere migliore o meno, ma potrebbe essere più semplice se non conosci le chiavi e non dovresti aggiungere troppa elaborazione.
Ricorda solo che quando aggiungi / elimini da uno devi farlo dall'altro o le cose diventeranno pazze eh: P