Comment savoir si une clé donnée existe dans un std :: map C ++

450

J'essaie de vérifier si une clé donnée est dans une carte et ne peut pas le faire quelque peu:

typedef map<string,string>::iterator mi;
map<string, string> m;
m.insert(make_pair("f","++--"));
pair<mi,mi> p = m.equal_range("f");//I'm not sure if equal_range does what I want
cout << p.first;//I'm getting error here

alors comment imprimer ce qui est en p?

Nous ne pouvons rien faire
la source
std::pair<iterator,bool> insert( const value_type& value );Quel est le bool qu'il renvoie? indique-t-elle si la clé est déjà présente ou non?
krithikaGopalakrisnan

Réponses:

691

Utilisation map::find

if ( m.find("f") == m.end() ) {
  // not found
} else {
  // found
}

la source
105
Si vous voulez simplement vérifier si une certaine clé existe, vous préférez probablement utilisermap::count
tomsmeding
10
@tomsmeding Il n'y a qu'une seule clé dans un std :: map. Le nombre sera donc égal à 0 ou 1. L'un est-il plus efficace que l'autre?
goelakash
34
@goelakash à peine; c'est juste que countrenvoie un intmoment findrenvoie un itérateur entier. Vous enregistrez la construction de l'itérateur :) Évidemment, si vous allez ensuite utiliser la valeur si elle existe, utilisez find et stockez son résultat.
tomsmeding
9
@tomsmeding Si vous utilisez un multimap, vous devrez parcourir l'intégralité du conteneur. Dans ce cas, find () peut être plus rapide.
Trevor Hickey
11
Pour ceux qui recherchent la vitesse: count et findsont presque identiques en vitesse lors de l'utilisation de cartes qui nécessitent des clés uniques. (1) Si vous n'avez pas besoin des éléments pour maintenir un ordre spécifique, utilisez std :: unordered_map , qui a des recherches presque constantes et peut être très bénéfique lors du stockage de plus de quelques paires. (2) Si vous souhaitez utiliser la valeur si elle existe, stockez le résultat de :: find et utilisez l'itérateur pour empêcher 2 recherches:auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
cdgraham
305

Pour vérifier s'il existe une clé particulière dans la carte, utilisez la countfonction membre de l'une des manières suivantes:

m.count(key) > 0
m.count(key) == 1
m.count(key) != 0

La documentation de map::finddit: "Une autre fonction membre,, map::countpeut être utilisée pour vérifier simplement si une clé particulière existe."

La documentation de map::countdit: "Parce que tous les éléments d'un conteneur de carte sont uniques, la fonction ne peut renvoyer que 1 (si l'élément est trouvé) ou zéro (sinon)."

Pour récupérer une valeur de la carte via une clé que vous savez exister, utilisez map :: at :

value = m.at(key)

Contrairement à map :: operator [] ,map::at ne créera pas de nouvelle clé dans la carte si la clé spécifiée n'existe pas.

DavidRR
la source
33
Si vous allez effectuer les deux opérations, vérifiez si elles existent et faites quelque chose à ce sujet. Utilisez findplutôt. L' secondattribut de l'itérateur retourné par findpeut être utilisé pour récupérer la valeur de la clé. Si vous utilisez countalors atou si operator[]vous effectuez deux opérations alors que vous n'auriez pu en utiliser qu'une seule.
OdraEncoded
1
Vous n'avez pas besoin de faire> 0, == 1 ou! = 0; c'est le contrôle exact que C ++ fait dans une instruction if (condition! = 0), vous pouvez donc simplement utiliserif(m.count(key))
jv110
6
@ jv110 Le compilateur Microsoft C ++ émet un avertissement lorsqu'il rencontre une conversion de intà bool. Bien qu'il existe d'autres compilateurs C ++ qui n'émettent pas d'avertissement similaire, je préfère utiliser une comparaison explicite pour clarifier l'intention et améliorer la lisibilité. Notez que d'autres langages tels que C # interdisent une telle conversion implicite pour éviter la possibilité d'introduire des erreurs de programmation subtiles.
DavidRR
quelle est la complexité temporelle du décompte? S'agit-il simplement d'une opération O (1)?
Mazeryt
1
@Mazeryt Étant donné que nous parlons d'une classe dans la bibliothèque standard C ++, je suppose certainement que oui. Pour une discussion indépendante de la langue de votre question, voir Les tables de hachage peuvent-elles vraiment être O (1)? .
DavidRR
47

C ++ 20 nous permet std::map::containsde le faire.

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> example = {{1, "One"}, {2, "Two"}, 
                                     {3, "Three"}, {42, "Don\'t Panic!!!"}};

    if(example.contains(42)) {
        std::cout << "Found\n";
    } else {
        std::cout << "Not found\n";
    }
}
Denis Sablukov
la source
34
Je suppose que je vais le dire: enfin.
Erik Campobadal
2
Il était temps .....
Ridhuvarshan
39

Vous pouvez utiliser .find():

map<string,string>::iterator i = m.find("f");

if (i == m.end()) { /* Not found */ }
else { /* Found, i->first is f, i->second is ++-- */ }
Thomas Bonini
la source
15
m.find == m.end() // not found 

Si vous souhaitez utiliser une autre API, recherchez go for m.count(c)>0

 if (m.count("f")>0)
      cout << " is an element of m.\n";
    else 
      cout << " is not an element of m.\n";
un J.
la source
12

Je pense que tu veux map::find. Si m.find("f")est égal à m.end(), la clé est introuvable. Sinon, find renvoie un itérateur pointant sur l'élément trouvé.

L'erreur est due au fait qu'il p.firsts'agit d'un itérateur, qui ne fonctionne pas pour l'insertion de flux. Modifiez votre dernière ligne en cout << (p.first)->first;. pest une paire d'itérateurs, p.firstest un itérateur, p.first->firstest la chaîne de clé.

Une carte ne peut avoir qu'un seul élément pour une clé donnée, elle equal_rangen'est donc pas très utile. Il est défini pour la carte, car il est défini pour tous les conteneurs associatifs, mais il est beaucoup plus intéressant pour le multimap.

Steve Jessop
la source
En fait, comme il s'agit d'une paire d'itérateurs sur une carte, elle doit être "cout << p.first-> first;"
stefaanv
J'ai corrigé ma réponse, merci. C'est ce que j'obtiens pour ne pas avoir compilé mon code. Et vous avez raison (dans un commentaire supprimé) de vérifier la validité, mais j'essayais simplement d'expliquer pourquoi il ne pouvait pas imprimer p.first, et ce n'est pas parce qu'il n'est pas valide - nous savons que "f" sera trouvé. Comme je ne recommande pas du tout d'utiliser equal_range, je ne suis pas sur le point d'afficher un code de vérification des erreurs pour cela.
Steve Jessop
Wow, vous scannez vraiment SO. Je ne faisais que l'ajouter pour être complet, car votre point était clair. J'ai ajouté la vérification de validité à ma réponse précédente, mais votre réponse m'a battu, alors je l'ai supprimée, car elle n'a pas ajouté grand-chose de toute façon, comme vous l'avez mentionné.
stefaanv
Oui, je ne l'ai vu que parce que votre commentaire est apparu lorsque j'ai posté le mien.
Steve Jessop
12

C++17simplifié un peu plus avec un If statement with initializer. De cette façon, vous pouvez avoir votre gâteau et le manger aussi.

if ( auto it{ m.find( "key" ) }; it != std::end( m ) ) 
{
    // Use `structured binding` to get the key
    // and value.
    auto[ key, value ] { *it };

    // Grab either the key or value stored in the pair.
    // The key is stored in the 'first' variable and
    // the 'value' is stored in the second.
    auto mkey{ it->first };
    auto mvalue{ it->second };

    // That or just grab the entire pair pointed
    // to by the iterator.
    auto pair{ *it };
} 
else 
{
   // Key was not found..
}
WBuck
la source
4
map<string, string> m;

la clé de contrôle existe ou non, et le nombre de retours se produit (0/1 sur la carte):

int num = m.count("f");  
if (num>0) {    
    //found   
} else {  
    // not found  
}

vérifier que la clé existe ou non, et retourner l'itérateur:

map<string,string>::iterator mi = m.find("f");  
if(mi != m.end()) {  
    //found  
    //do something to mi.  
} else {  
    // not found  
}  

dans votre question, l'erreur causée par une mauvaise operator<<surcharge, car p.firstest map<string, string>, vous ne pouvez pas l' imprimer. essaye ça:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}
hustljian
la source
1
Vous avez une faute de frappe. Changer "cout" en "count"
Rivka
1
Et cette faute de frappe peut vraiment décourager quelqu'un, car cela coutpeut signifier quelque chose de très différent decount
modulitos
4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Bien sûr, si vous vouliez devenir plus amateur, vous pouvez toujours créer un modèle pour une fonction qui a également pris une fonction trouvée et une fonction non trouvée, quelque chose comme ceci:

template <typename T, typename Key, typename FoundFunction, typename NotFoundFunction>
void find_and_execute(const T& container, const Key& key, FoundFunction found_function, NotFoundFunction not_found_function)
{
    auto& it = container.find(key);
    if (it != std::end(container))
    {
        found_function(key, it->second);
    }
    else
    {
        not_found_function(key);
    }
}

Et utilisez-le comme ceci:

    std::map<int, int> some_map;
    find_and_execute(some_map, 1,
        [](int key, int value){ std::cout << "key " << key << " found, value: " << value << std::endl; },
        [](int key){ std::cout << "key " << key << " not found" << std::endl; });

L'inconvénient est de trouver un bon nom, "find_and_execute" est maladroit et je ne peux rien trouver de mieux du haut de ma tête ...

Lambage
la source
3

Soyez prudent en comparant le résultat de la recherche avec la fin comme pour la carte 'm' comme toutes les réponses l'ont fait ci-dessus map :: iterator i = m.find ("f");

 if (i == m.end())
 {
 }
 else
 {
 }  

vous ne devez pas essayer d'effectuer une opération telle que l'impression de la clé ou de la valeur avec l'itérateur i si son égal à m.end () sinon cela entraînera un défaut de segmentation.

Invictus
la source
0

En comparant le code de std :: map :: find et std :: map :: count, je dirais que le premier peut donner un avantage en termes de performances:

const_iterator find(const key_type& _Keyval) const
    {   // find an element in nonmutable sequence that matches _Keyval
    const_iterator _Where = lower_bound(_Keyval); // Here one looks only for lower bound
    return (_Where == end()
        || _DEBUG_LT_PRED(this->_Getcomp(),
            _Keyval, this->_Key(_Where._Mynode()))
                ? end() : _Where);
    }

size_type count(const key_type& _Keyval) const
    {   // count all elements that match _Keyval
    _Paircc _Ans = equal_range(_Keyval); // Here both lower and upper bounds are to be found, which is presumably slower.
    size_type _Num = 0;
    _Distance(_Ans.first, _Ans.second, _Num);
    return (_Num);
    }
Espérer
la source
0

Je sais que cette question a déjà de bonnes réponses, mais je pense que ma solution mérite d'être partagée.

Il fonctionne pour les deux std::mapet std::vector<std::pair<T, U>>est disponible à partir de C ++ 11.

template <typename ForwardIterator, typename Key>
bool contains_key(ForwardIterator first, ForwardIterator last, Key const key) {
    using ValueType = typename std::iterator_traits<ForwardIterator>::value_type;

    auto search_result = std::find_if(
        first, last,
        [&key](ValueType const& item) {
            return item.first == key;
        }
    );

    if (search_result == last) {
        return false;
    } else {
        return true;
    }
}
Casse Noisette
la source
-5

Si vous souhaitez comparer une paire de cartes, vous pouvez utiliser cette méthode:

typedef map<double, double> TestMap;
TestMap testMap;
pair<map<double,double>::iterator,bool> controlMapValues;

controlMapValues= testMap.insert(std::pair<double,double>(x,y));
if (controlMapValues.second == false )
{
    TestMap::iterator it;
    it = testMap.find(x);

    if (it->second == y)
    {
        cout<<"Given value is already exist in Map"<<endl;
    }
}

C'est une technique utile.

EmreS
la source
En tant que débutant en programmation C ++, je suis vraiment curieux de savoir pourquoi cette réponse est downvoted. Pourquoi cette réponse est-elle impopulaire?
gromit190
3
@ gromit190 car il utilise une toute autre structure de données pour voir si la clé existe lorsque std :: map a déjà cette capacité. Cela nécessiterait également une synchronisation entre les deux structures de données, une dépendance que personne ne veut traiter.
Lambage
-5
map <int , char>::iterator itr;
    for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
    {
        if (itr->second == 'c')
        {
            cout<<itr->first<<endl;
        }
    }
Muhammad Ahmad Zafar
la source
3
Veuillez développer votre code. Un extrait de code sans aucune explication n'a pas tendance à être utile à long terme.
iBug