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.