L'idée d'obtenir un itérateur vers les valeurs est de l'utiliser dans des algorithmes STL, par exemple, l'intersection des clés de deux cartes. La solution impliquant Boost ne le permet pas, car elle produira un itérateur Boost. La pire réponse obtient le plus de votes!
Réponses:
70
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.
Mais alors ce sera vraiment une mauvaise idée d'exposer l'itérateur du vecteur à l'extérieur.
Naveen
N'exposez pas l'itérateur. Fournissez simplement les clés en vecteur
aJ.
5
Vous voudrez peut-être faire ceci à la place: const Key& k(iter->first);
strickli
17
Deux choses, cela répond à la question de l' OP avec exactement la réponse qu'il connaissait déjà et ne cherchait pas, d' autre part , cette méthode ne vous aidera pas si vous voulez faire quelque chose comme: std::vector<Key> v(myMap.begin(), myMap.end()).
Andreas Magnusson
Ne convertissez pas les clés en vecteur. Créer un nouveau vecteur va à l'encontre de l'objectif de l'itération, qui est censé être rapide et n'allouer rien. En outre, ce sera lent pour les grands ensembles.
Kevin Chen
85
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é.
Malheureusement, la norme C ++ 17 vous oblige à déclarer la valuevariable, même si vous ne l'utilisez pas ( std::ignorecomme on l'utiliserait pour std::tie(..)ne fonctionne pas, voir cette discussion ).
Certains compilateurs peuvent donc vous avertir de la valuevariable 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.
ne pouvez-vous pas l'assigner à std :: ignore en principe? Cela nuirait-il réellement à l'efficacité du code compilé ou serait-il en fait une valeur nulle? (Je ne veux pas dire dans la liaison mais plutôt comme une action dans la boucle)
KotoroShinoto
Depuis C ++ 17, vous pouvez également utiliser [[peut-être_unused]]. Cela supprime l'avertissement. Comme ça:for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
arhuaco
15
Ci-dessous la solution modèle plus générale à laquelle Ian a fait référence ...
Lorsqu'aucune explicite beginet endn'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;
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();for(; iter != endIter;++iter){
type key = iter->first;.....}
Oui, je sais, le problème est que j'ai une classe A {public: // je voudrais exposer un itérateur sur les clés de la carte privée ici private: map <>};
Bogdan Balan
Dans ce cas, je pense que vous pouvez créer une std :: list en utilisant std :: trasnform et en ne récupérant que les clés de la carte. Ensuite, vous pouvez exposer l'itérateur de liste car l'insertion de plus d'éléments dans la liste n'invalidera pas les itérateurs existants.
Naveen
3
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_iteratorpour envelopper l'itérateur dans un qui ne renvoie que les valeurs / clés.
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.
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".
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
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 source
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.
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.
la source
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é.
la source
Sans Boost
Vous pouvez le faire en étendant simplement l'itérateur STL pour cette carte. Par exemple, un mappage de chaînes en ints:
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()
etend()
.la source
template<typename C> class key_iterator : public C::iterator
, etcAvec 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):
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 pourstd::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.la source
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 ...
Tous les crédits vont à Ian ... Merci Ian.
la source
Vous recherchez map_keys , avec lui, vous pouvez écrire des choses comme
la source
BOOST_FOREACH(const key_t& key, ...
Voici un exemple de la façon de le faire en utilisant le transform_iterator de Boost
la source
Lorsqu'aucune explicite
begin
etend
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 avecla source
Tu veux faire ça?
la source
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.la source
Vous pourriez
std::map<K,V>::iterator
std::transform
de votremap.begin()
tomap.end()
avec unboost::bind( &pair::second, _1 )
foncteur->second
membre lors de l'itération avec unefor
boucle.la source
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.la source
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.
la source
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:
Si vous souhaitez parcourir les valeurs, utilisez
t.second
dans le fichier lambda.la source
Beaucoup de bonnes réponses ici, ci-dessous est une approche en utilisant quelques-unes d'entre elles qui vous permet d'écrire ceci:
Si c'est ce que vous avez toujours voulu, voici le code de MapKeys ():
la source
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*
la source
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 ...
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
la source