Determinare se la mappa contiene un valore per una chiave?


256

Qual è il modo migliore per determinare se una mappa STL contiene un valore per una determinata chiave?

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

}

Esaminandolo in un debugger, sembra che itersiano solo dati inutili.

Se decomprimere questa riga:

Bar b2 = m[2]

Il debugger mostra che lo b2è {i = 0}. (Suppongo significhi che l'utilizzo di un indice indefinito restituirà una struttura con tutti i valori vuoti / non inizializzati?)

Nessuno di questi metodi è così eccezionale. Quello che mi piacerebbe davvero è un'interfaccia come questa:

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

Esiste qualcosa di simile?


Risposte:


274

Esiste qualcosa di simile?

No. Con la classe stl map, si utilizza ::find()per cercare la mappa e confrontare l'iteratore restituito constd::map::end()

così

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

Ovviamente puoi scrivere la tua getValue()routine se vuoi (anche in C ++, non c'è motivo di usarlo out), ma sospetterei che una volta che avrai imparato a usare std::map::find()non vorrai perdere tempo.

Anche il tuo codice è leggermente sbagliato:

m.find('2');cercherà un valore chiave nella mappa '2'. IIRC il compilatore C ++ convertirà implicitamente '2' in un int, il che si tradurrà nel valore numerico per il codice ASCII per '2' che non è quello che vuoi.

Dato che il tuo tipo di chiave in questo esempio è intche vuoi cercare in questo modo:m.find(2);


7
Come mai? findindica l'intenzione molto meglio di quanto countnon faccia. Più oltre, countnon restituisce l'articolo. Se leggi la domanda del PO, vuole verificare l'esistenza e restituire l'elemento. findfa quello. countnon.
Alan,

if (m.count (chiave)) b3 = m [chiave]; // o altro
pconnell il

64
Sono sempre stato curioso di sapere che tipo di erba fumasse le persone che hanno progettato l'intera API stl.
Trappola

2
Alan devo essere d'accordo con @dynamic su questo, dover definire un iteratore e poi confrontarlo con end non è un modo naturale per dire che qualcosa non esiste. Mi sembra molto più semplice dire che un certo elemento appare almeno una volta in questa mappa. Questo è ciò che conta.
PiersyP,

1
@Claudiu C ++ 20 aggiunge proprio questo.
pooya13

330

Finché la mappa non è una multimappa, uno dei modi più eleganti sarebbe usare il metodo di conteggio

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

Il conteggio sarebbe 1 se l'elemento è effettivamente presente nella mappa.


22
Questo non controllerà tutte le chiavi anche se ne ha già trovata una? Che può diventare costoso velocemente ...
mmdanziger

35
Conterà solo più di una chiave se utilizzata su una multimappa.
Andrew Prock,

14
@mmdanziger No, non sarà costoso: cplusplus.com/reference/map/map/count Il conteggio ha dimensioni logaritmiche.
jgyou,

28
La chiave esiste, e poi cosa? A quel punto di solito vorresti ottenerne il valore, pagando per un'altra ricerca (es. Usando operator[]). findti dà la TryGetValuesemantica di .NET , che è quasi sempre ciò che tu (e in particolare l'OP) desideri.
Ohad Schneider,

2
@serine Capito. Si noti che nel caso in cui manchi la chiave nel rilascio, il comportamento sarà diverso, poiché map [key] restituirà un valore di elemento di nuova creazione predefinito.
Ohad Schneider,

53

Esiste già con find, non solo in quella sintassi esatta.

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

Se vuoi accedere al valore se esiste, puoi fare:

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

Con C ++ 0x e auto, la sintassi è più semplice:

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

Ti consiglio di abituarti piuttosto che provare a trovare un nuovo meccanismo per semplificarlo. Potresti riuscire a ridurre un po 'di codice, ma considera il costo per farlo. Ora hai introdotto una nuova funzione che le persone che hanno familiarità con C ++ non saranno in grado di riconoscere.

Se vuoi implementarlo comunque nonostante questi avvertimenti, allora:

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

38

Ho appena notato che con C ++ 20 avremo

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

Ciò tornerà vero se map contiene un elemento con chiave key.


Finalmente una risposta che parla di questa funzione! (C ++ 20)
Samuel Rasquinha,

Finalmente ? Grazie, ma ha quasi 2 anni! ;-)
kebs

7

amap.findritorna amap::endquando non trova quello che stai cercando - dovresti verificarlo.


4

Controllare il valore di ritorno di findcontro end.

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

1

Puoi creare la tua funzione getValue con il seguente codice:

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

Credo che la linea 6 dovrebbe essereout = foundIter->second
Dithermaster il

Ho corretto la risposta di Kip per mostrare correttamente out = foundIter->secondpiuttosto cheout = *foundIter
netjeff

1

Riassumendo sinteticamente alcune delle altre risposte:

Se non stai ancora utilizzando C ++ 20, puoi scrivere la tua mapContainsKeyfunzione:

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

Se desideri evitare molti sovraccarichi per mapvs unordered_mape tipi di chiave e valore diversi, puoi renderlo una templatefunzione.

Se stai usando C++ 20o più tardi, ci sarà una containsfunzione integrata:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

if (myMap.contains(key))
{
  // stuff here
}

-1

Se vuoi determinare se una chiave è presente nella mappa o meno, puoi usare la funzione membro find () o count () della mappa. La funzione find che viene usata qui nell'esempio restituisce l'iteratore in element o map :: end altrimenti. In caso di conteggio il conteggio restituisce 1 se trovato, altrimenti restituisce zero (o in altro modo).

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

-1

Boost multindex può essere utilizzato per una soluzione adeguata. La seguente soluzione non è un'opzione ottimale ma potrebbe essere utile in alcuni casi in cui l'utente sta assegnando un valore predefinito come 0 o NULL all'inizializzazione e desidera verificare se il valore è stato modificato.

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