Existe-t-il un moyen d'itérer sur les clés, pas sur les paires d'une carte C ++?
Existe-t-il un moyen d'itérer sur les clés, pas sur les paires d'une carte C ++?
Réponses:
Si vous avez vraiment besoin de masquer la valeur renvoyée par l'itérateur "réel" (par exemple parce que vous voulez utiliser votre itérateur de clé avec des algorithmes standard, afin qu'ils agissent sur les clés au lieu des paires), alors jetez un œil à Boost's transform_iterator .
[Astuce: lorsque vous regardez la documentation Boost pour une nouvelle classe, lisez d'abord les "exemples" à la fin. Vous avez alors une chance sportive de comprendre de quoi diable le reste parle :-)]
la carte est un conteneur associatif. Par conséquent, l'itérateur est une paire de clés, val. SI vous n'avez besoin que de clés, vous pouvez ignorer la partie valeur de la paire.
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k = iter->first;
//ignore value
//Value v = iter->second;
}
EDIT:: Si vous souhaitez exposer uniquement les clés à l'extérieur, vous pouvez convertir la carte en vecteur ou en clés et l'exposer.
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.
Avec C ++ 11, la syntaxe d'itération est simple. Vous continuez à parcourir des paires, mais il est facile d'accéder uniquement à la clé.
#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";
}
}
Vous pouvez le faire en étendant simplement l'itérateur STL pour cette carte. Par exemple, un mappage de chaînes en 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; }
};
Vous pouvez également effectuer cette extension dans un modèle , pour une solution plus générale.
Vous utilisez votre itérateur exactement comme vous le feriez avec un itérateur de liste, sauf que vous itérez sur la carte begin()
et 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
Avec C ++ 17, vous pouvez utiliser une liaison structurée dans une boucle for basée sur une plage (en adaptant la réponse de John H. en conséquence):
#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';
}
}
Malheureusement, la norme C ++ 17 vous oblige à déclarer la value
variable, même si vous ne l'utilisez pas ( std::ignore
comme on l'utiliserait pour std::tie(..)
ne fonctionne pas, voir cette discussion ).
Certains compilateurs peuvent donc vous avertir de la value
variable inutilisée ! Les avertissements au moment de la compilation concernant les variables inutilisées sont interdits pour tout code de production dans mon esprit. Ainsi, cela peut ne pas être applicable pour certaines versions de compilateur.
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Ci-dessous la solution modèle plus générale à laquelle Ian a fait référence ...
#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; }
};
Tous les crédits vont à Ian ... Merci Ian.
Voici un exemple de la façon de le faire en utilisant le 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;
}
}
Lorsqu'aucune explicite begin
et end
n'est nécessaire, c'est-à-dire pour la boucle de plage, la boucle sur les clés (premier exemple) ou les valeurs (deuxième exemple) peut être obtenue avec
#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;
Tu veux faire ça?
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 vous avez besoin d'un itérateur qui ne renvoie que les clés, vous devez envelopper l'itérateur de la carte dans votre propre classe qui fournit l'interface souhaitée. Vous pouvez déclarer une nouvelle classe d'itérateur à partir de zéro comme ici , ou utiliser les constructions d'assistance existantes. Cette réponse montre comment utiliser Boost transform_iterator
pour envelopper l'itérateur dans un qui ne renvoie que les valeurs / clés.
Cette réponse est comme celle de rodrigob sauf sans le BOOST_FOREACH
. Vous pouvez utiliser la plage de c ++ basée sur à la place.
#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;
}
}
Sans Boost, vous pourriez le faire comme ça. Ce serait bien si vous pouviez écrire un opérateur de conversion au lieu de getKeyIterator (), mais je ne peux pas le faire compiler.
#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());
}
}
Pour la postérité, et comme j'essayais de trouver un moyen de créer une plage, une alternative est d'utiliser boost :: adapters :: transform
Voici un petit exemple:
#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 vous souhaitez parcourir les valeurs, utilisez t.second
dans le fichier lambda.
Beaucoup de bonnes réponses ici, ci-dessous est une approche en utilisant quelques-unes d'entre elles qui vous permet d'écrire ceci:
void main()
{
std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
for (auto key : MapKeys(m))
std::cout << key << std::endl;
}
Si c'est ce que vous avez toujours voulu, voici le code de 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);
}
J'ai adopté la réponse d'Ian pour travailler avec tous les types de cartes et j'ai corrigé le renvoi d'une référence pour 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;
}
};
Je sais que cela ne répond pas à votre question, mais une option que vous voudrez peut-être envisager est d'avoir simplement deux vecteurs avec le même index contenant des informations "liées".
Donc dans ...
std::vector<std::string> vName;
std::vector<int> vNameCount;
si vous voulez le nombre de noms par nom, vous faites simplement votre boucle rapide pour sur vName.size (), et lorsque vous le trouvez, c'est l'index de vNameCount que vous recherchez.
Bien sûr, cela peut ne pas vous donner toutes les fonctionnalités de la carte, et cela peut être mieux ou pas, mais cela pourrait être plus facile si vous ne connaissez pas les clés et ne devrait pas ajouter trop de traitement.
N'oubliez pas que lorsque vous ajoutez / supprimez de l'un, vous devez le faire de l'autre ou les choses deviendront folles heh: P