Déterminer si la carte contient une valeur pour une clé?

256

Quelle est la meilleure façon de déterminer si une carte STL contient une valeur pour une clé donnée?

#include <map>

using namespace std;

struct Bar
{
    int i;
};

int main()
{
    map<int, Bar> m;
    Bar b = {0};
    Bar b1 = {1};

    m[0] = b;
    m[1] = b1;

    //Bar b2 = m[2];
    map<int, Bar>::iterator iter = m.find(2);
    Bar b3 = iter->second;

}

En examinant cela dans un débogueur, il semble que ce ne itersoient que des données poubelles.

Si je commente cette ligne:

Bar b2 = m[2]

Les spectacles de débogage qui b2est {i = 0}. (Je suppose que cela signifie que l'utilisation d'un index non défini retournera une structure avec toutes les valeurs vides / non initialisées?)

Aucune de ces méthodes n'est si géniale. Ce que j'aimerais vraiment, c'est une interface comme celle-ci:

bool getValue(int key, Bar& out)
{
    if (map contains value for key)
    {
        out = map[key];
        return true;
    }
    return false;
}

Existe-t-il quelque chose dans ce sens?

Nick Heiner
la source

Réponses:

274

Existe-t-il quelque chose dans ce sens?

Non. Avec la classe de carte stl, vous utilisez ::find()pour rechercher la carte et comparer l'itérateur retourné àstd::map::end()

alors

map<int,Bar>::iterator it = m.find('2');
Bar b3;
if(it != m.end())
{
   //element found;
   b3 = it->second;
}

Évidemment, vous pouvez écrire votre propre getValue()routine si vous le souhaitez (également en C ++, il n'y a aucune raison de l'utiliser out), mais je soupçonnerais qu'une fois que vous maîtriserez l'utilisation, std::map::find()vous ne voudrez pas perdre votre temps.

De plus, votre code est légèrement faux:

m.find('2');recherchera sur la carte une valeur clé qui est '2'. IIRC le compilateur C ++ convertira implicitement «2» en un int, ce qui entraîne la valeur numérique du code ASCII pour «2» qui n'est pas ce que vous voulez.

Étant donné que votre type de clé dans cet exemple est que intvous souhaitez rechercher comme ceci:m.find(2);

Alan
la source
7
Comment? findindique une intention bien meilleure que la countnôtre. De plus, countne retourne pas l'article. Si vous lisez la question de l'OP, il veut vérifier l'existence et renvoyer l'élément. findest-ce que. countne fait pas.
Alan
if (m.count (clé)) b3 = m [clé]; // ou autre chose
pconnell
64
J'ai toujours été curieux de savoir quel type d'herbe fumait les gens qui ont conçu toute l'API stl.
Piège
2
Alan Je dois être d'accord avec @dynamic sur celui-ci, devoir définir un itérateur et ensuite le comparer avec fin n'est pas une façon naturelle de dire que quelque chose n'existe pas. Il me semble beaucoup plus simple de dire qu'un certain élément apparaît au moins une fois sur cette carte. C'est ce qui compte.
PiersyP
1
@Claudiu C ++ 20 ajoute juste cela.
pooya13
330

Tant que la carte n'est pas une carte multiple, l'un des moyens les plus élégants serait d'utiliser la méthode de comptage

if (m.count(key))
    // key exists

Le compte serait de 1 si l'élément est bien présent sur la carte.

pconnell
la source
22
Cela ne vérifiera-t-il pas toutes les clés même s'il en a déjà trouvé une? Cela peut coûter cher rapidement ...
mmdanziger
35
Il ne comptera que plusieurs clés s'il est utilisé sur une carte multiple.
Andrew Prock
14
@mmdanziger Non, ce ne sera pas cher: cplusplus.com/reference/map/map/count Count est de taille logarithmique.
jgyou
28
La clé existe, et puis quoi? À ce stade, vous voudriez généralement en obtenir la valeur, en payant pour une autre recherche (par exemple en utilisant operator[]). findvous donne la TryGetValuesémantique de .NET , qui est presque toujours ce que vous (et plus précisément l'OP) voulez.
Ohad Schneider
2
@serine Compris. Notez que si la clé est manquante dans la version, le comportement sera différent, car map [clé] renverra une nouvelle valeur d'élément construite par défaut.
Ohad Schneider
53

Il existe déjà avec find seulement pas dans cette syntaxe exacte.

if (m.find(2) == m.end() )
{
    // key 2 doesn't exist
}

Si vous souhaitez accéder à la valeur si elle existe, vous pouvez faire:

map<int, Bar>::iterator iter = m.find(2);
if (iter != m.end() )
{
    // key 2 exists, do something with iter->second (the value)
}

Avec C ++ 0x et auto, la syntaxe est plus simple:

auto iter = m.find(2);
if (iter != m.end() )
{
    // key 2 exists, do something with iter->second (the value)
}

Je vous recommande de vous y habituer plutôt que d'essayer de trouver un nouveau mécanisme pour le simplifier. Vous pourriez être en mesure de réduire un peu de code, mais considérez le coût de le faire. Vous avez maintenant introduit une nouvelle fonction que les personnes familiarisées avec C ++ ne pourront pas reconnaître.

Si vous souhaitez malgré tout implémenter cela malgré ces avertissements, alors:

template <class Key, class Value, class Comparator, class Alloc>
bool getValue(const std::map<Key, Value, Comparator, Alloc>& my_map, int key, Value& out)
{
    typename std::map<Key, Value, Comparator, Alloc>::const_iterator it = my_map.find(key);
    if (it != my_map.end() )
    {
        out = it->second;
        return true;
    }
    return false;
}
stinky472
la source
39

Je viens de remarquer qu'avec C ++ 20 , nous aurons

bool std::map::contains( const Key& key ) const;

Cela retournera vrai si map contient un élément avec clé key.

kebs
la source
Enfin une réponse qui parle de cette fonction! (C ++ 20)
Samuel Rasquinha
Finalement ? Merci, mais ça fait presque 2 ans! ;-)
kebs
1
J'espère que les gens vont faire défiler vers cette solution et ne plus utiliser les obsolètes. :)
Leśny Rumcajs il y a
7

amap.findrevient amap::endquand il ne trouve pas ce que vous cherchez - vous êtes censé vérifier cela.

Alex Martelli
la source
4

Vérifiez la valeur de retour de findcontre end.

map<int, Bar>::iterator it = m.find('2');
if ( m.end() != it ) { 
  // contains
  ...
}
JaredPar
la source
1

Vous pouvez créer votre fonction getValue avec le code suivant:

bool getValue(const std::map<int, Bar>& input, int key, Bar& out)
{
   std::map<int, Bar>::iterator foundIter = input.find(key);
   if (foundIter != input.end())
   {
      out = foundIter->second;
      return true;
   }
   return false;
}
Kip Streithorst
la source
Je pense que la ligne 6 devrait êtreout = foundIter->second
Dithermaster
J'ai corrigé la réponse de Kip pour qu'elle s'affiche correctement out = foundIter->secondplutôt queout = *foundIter
netjeff
1

Pour résumer brièvement certaines des autres réponses:

Si vous n'utilisez pas encore C ++ 20, vous pouvez écrire votre propre mapContainsKeyfonction:

bool mapContainsKey(std::map<int, int>& map, int key)
{
  if (map.find(key) == map.end()) return false;
  return true;
}

Si vous souhaitez éviter de nombreuses surcharges pour mapvs unordered_mapet différents types de clés et de valeurs, vous pouvez en faire une templatefonction.

Si vous utilisez C++ 20ou plus tard, il y aura une containsfonction intégrée:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

if (myMap.contains(key))
{
  // stuff here
}
cdahms
la source
-1

Si vous souhaitez déterminer si une clé se trouve dans la carte ou non, vous pouvez utiliser la fonction membre find () ou count () de la carte. La fonction find qui est utilisée ici dans l'exemple renvoie sinon l'itérateur à element ou map :: end. En cas de comptage, le comptage renvoie 1 s'il est trouvé, sinon il renvoie zéro (ou autre).

if(phone.count(key))
{ //key found
}
else
{//key not found
}

for(int i=0;i<v.size();i++){
    phoneMap::iterator itr=phone.find(v[i]);//I have used a vector in this example to check through map you cal receive a value using at() e.g: map.at(key);
    if(itr!=phone.end())
        cout<<v[i]<<"="<<itr->second<<endl;
    else
        cout<<"Not found"<<endl;
}
Prashant Shubham
la source
-1

Boost multindex peut être utilisé pour une solution appropriée. La solution suivante n'est pas une meilleure option mais peut être utile dans quelques cas où l'utilisateur attribue une valeur par défaut comme 0 ou NULL à l'initialisation et veut vérifier si la valeur a été modifiée.

Ex.
< int , string >
< string , int > 
< string , string > 

consider < string , string >
mymap["1st"]="first";
mymap["second"]="";
for (std::map<string,string>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
{
       if ( it->second =="" ) 
            continue;
}
user2761565
la source