Come scoprire se esiste una determinata chiave in una std :: map C ++


450

Sto cercando di verificare se una determinata chiave si trova in una mappa e in qualche modo non riesco a farlo:

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

quindi come posso stampare ciò che è in p?


std::pair<iterator,bool> insert( const value_type& value );Qual è il valore che restituisce? dice se la chiave è già presente o no?
Krithika Gopalakrisnan,

Risposte:


691

Uso map::find

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

105
Se vuoi solo controllare se esiste una determinata chiave, probabilmente preferiresti usaremap::count
tomsmeding

10
@tomsmeding C'è una sola chiave in una std :: map. Quindi il conteggio sarà 0 o 1. L'uno è più efficiente dell'altro?
goelakash,

34
@goelakash quasi; è solo che countrestituisce un intpo ' findrestituisce un intero iteratore. Salva la costruzione dell'iteratore :) Ovviamente, se in seguito utilizzerai il valore se esiste, usa trova e memorizza il suo risultato.
Tomsmeding,

9
@tomsmeding Se stai usando una multimappa, dovresti guardare attraverso l'intero contenitore. In tal caso, find () potrebbe essere più veloce.
Trevor Hickey,

11
Per coloro che cercano la velocità: count e findsono quasi identici nella velocità quando si usano mappe che richiedono chiavi uniche. (1) Se non hai bisogno degli elementi per mantenere un ordine specifico, usa std :: unordered_map , che ha ricerche quasi costanti e può essere molto utile quando si memorizzano più coppie. (2) Se si desidera utilizzare il valore se esiste, memorizzare il risultato di :: find e utilizzare l'iteratore per impedire 2 auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
ricerche

305

Per verificare se esiste una chiave particolare nella mappa, utilizzare la countfunzione membro in uno dei seguenti modi:

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

La documentazione per map::finddice: "Un'altra funzione membro map::count, può essere utilizzata per controllare solo se esiste una chiave particolare".

La documentazione per map::countdice: "Poiché tutti gli elementi in un contenitore della mappa sono unici, la funzione può restituire solo 1 (se l'elemento viene trovato) o zero (altrimenti)."

Per recuperare un valore dalla mappa tramite una chiave che sai esistere, usa map :: at :

value = m.at(key)

A differenza di map :: operator [] , map::atnon creerà una nuova chiave nella mappa se la chiave specificata non esiste.


33
Se hai intenzione di fare entrambe le operazioni, controlla se esiste e poi fai qualcosa al riguardo. Usa findinvece. L' secondattributo dell'iteratore restituito da findpuò essere utilizzato per recuperare il valore della chiave. Se usi countallora ato operator[]stai eseguendo due operazioni quando avresti potuto usarne solo una.
OdraCodificato il

1
Non è necessario eseguire> 0, == 1 o! = 0; questo è il controllo esatto che fa C ++ in un'istruzione if (condizione! = 0), quindi puoi semplicemente usareif(m.count(key))
jv110

6
@ jv110 Il compilatore di Microsoft C ++ emette un avviso quando incontra un cast da inta bool. Sebbene ci siano altri compilatori C ++ che non emettono un avviso simile, preferisco usare un confronto esplicito per chiarire l'intento e migliorare la leggibilità. Si noti che altri linguaggi come C # vietano una tale conversione implicita per impedire la possibilità di introdurre sottili errori di programmazione.
DavidRR

qual è la complessità temporale del conteggio? È solo un'operazione O (1)?
Mazeryt,

1
@Mazeryt Dato che stiamo parlando di una classe nella libreria standard C ++, certamente lo presumo. Per una discussione indipendente dalla lingua della tua domanda, vedi Le tabelle hash possono davvero essere O (1)? .
DavidRR,

47

C ++ 20 ci dà std::map::containsper farlo.

#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";
    }
}

34
Immagino che lo dirò: finalmente.
Erik Campobadal,

2
A proposito di tempo .....
Ridhuvarshan,

39

Puoi usare .find():

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

if (i == m.end()) { /* Not found */ }
else { /* Found, i->first is f, i->second is ++-- */ }

15
m.find == m.end() // not found 

Se vuoi usare altre API, trova find for 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";

12

Penso che tu voglia map::find. Se m.find("f")è uguale am.end() , la chiave non è stata trovata. Altrimenti, trova restituisce un iteratore che punta all'elemento trovato.

L'errore è perché p.firstè un iteratore, che non funziona per l'inserimento del flusso. Cambia l'ultima riga in cout << (p.first)->first;. pè una coppia di iteratori, p.firstè un iteratore,p.first->first è la stringa chiave.

Una mappa può avere sempre e solo un elemento per una determinata chiave, quindi equal_rangenon è molto utile. È definito per la mappa, perché è definito per tutti i contenitori associativi, ma è molto più interessante per la multimappa.


In realtà, poiché è una coppia di iteratori di una mappa, dovrebbe essere "cout << p.first-> first;"
stefaanv,

Ho risolto la mia risposta, grazie. Questo è quello che ottengo non compilando il mio codice. E hai ragione (in un commento cancellato) sul controllo della validità, ma stavo solo cercando di spiegare perché non è riuscito a stampare p. Prima, e non è perché non è valido - sappiamo che "f" sarà trovato. Dato che non consiglio affatto di usare equal_range, non sto per mostrare il codice di controllo degli errori per quello.
Steve Jessop,

Wow, stai davvero scannerizzando SO. Lo stavo solo aggiungendo per completezza, perché il tuo punto era chiaro. Ho aggiunto il controllo di validità alla mia risposta precedente, ma la tua risposta mi ha battuto, quindi l'ho eliminato, perché non ha aggiunto molto, come hai detto.
Stefaanv,

Sì, l'ho visto solo perché il tuo commento è apparso quando ho pubblicato il mio.
Steve Jessop,

12

C++17semplificato un po 'di più con un If statement with initializer. In questo modo puoi avere la tua torta e mangiarla anche tu.

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..
}

4
map<string, string> m;

la chiave di controllo esiste o no e restituisce il numero di occorrenze (0/1 nella mappa):

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

la chiave di controllo esiste o no e restituisce iteratore:

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

nella tua domanda, l'errore causato da un operator<<sovraccarico grave , perché p.firstè map<string, string>impossibile stamparlo. prova questo:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}

1
Hai un refuso. Cambia "cout" in "count"
Rivka

1
E quel refuso può davvero buttare via qualcuno, dato che coutpuò significare qualcosa di molto diverso dacount
modulitos

4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Naturalmente, se volevi diventare più elaborato, potresti sempre creare una funzione che ha anche una funzione trovata e una funzione non trovata, qualcosa del genere:

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);
    }
}

E usalo in questo modo:

    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; });

Il rovescio della medaglia a questo sta venendo fuori con un buon nome, "find_and_execute" è imbarazzante e non riesco a trovare niente di meglio dalla cima della mia testa ...


3

Fai attenzione nel confrontare il risultato della ricerca con la fine come per la mappa 'm' come tutte le risposte hanno fatto sopra la mappa :: iteratore i = m.find ("f");

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

non si dovrebbe tentare di eseguire alcuna operazione come la stampa della chiave o del valore con iteratore i se è uguale a m.end () altrimenti causerà un errore di segmentazione.


0

Confrontando il codice di std :: map :: find e std :: map :: count, direi che il primo potrebbe produrre qualche vantaggio in termini di prestazioni:

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);
    }

0

So che questa domanda ha già delle buone risposte, ma penso che valga la pena condividere la mia soluzione.

Funziona per entrambi std::maped std::vector<std::pair<T, U>>è disponibile da 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;
    }
}

-5

Se vuoi confrontare una coppia di mappe puoi usare questo metodo:

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;
    }
}

Questa è una tecnica utile.


Come principiante con la programmazione C ++, sono davvero curioso di sapere perché questa risposta è stata sottoposta a downgrade. Perché questa risposta è impopolare?
gromit190,

3
@ gromit190 perché utilizza un'intera altra struttura di dati per vedere se la chiave esiste quando std :: map ha già questa capacità. Ciò richiederebbe anche la sincronizzazione tra le due strutture di dati, che è una dipendenza che nessuno vuole affrontare.
Lambage,

-5
map <int , char>::iterator itr;
    for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
    {
        if (itr->second == 'c')
        {
            cout<<itr->first<<endl;
        }
    }

3
Si prega di elaborare il codice. Uno snippet senza alcuna spiegazione non tende ad essere utile a lungo termine.
iBug
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.