Bestimmen Sie, ob die Karte einen Wert für einen Schlüssel enthält?


256

Wie lässt sich am besten feststellen, ob eine STL-Zuordnung einen Wert für einen bestimmten Schlüssel enthält?

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

}

Wenn man dies in einem Debugger untersucht, sieht es so aus, als wären es iternur Mülldaten.

Wenn ich diese Zeile auskommentiere:

Bar b2 = m[2]

Der Debugger zeigt , dass b2ist {i = 0}. (Ich vermute, es bedeutet, dass die Verwendung eines undefinierten Index eine Struktur mit allen leeren / nicht initialisierten Werten zurückgibt?)

Keine dieser Methoden ist so großartig. Was ich wirklich möchte, ist eine Schnittstelle wie diese:

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

Gibt es etwas in dieser Richtung?


Antworten:


274

Gibt es etwas in dieser Richtung?

Nein. Mit der stl-Kartenklasse ::find()durchsuchen Sie die Karte und vergleichen den zurückgegebenen Iterator mitstd::map::end()

so

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

Natürlich können Sie Ihre eigene getValue()Routine schreiben, wenn Sie möchten (auch in C ++ gibt es keinen Grund zur Verwendung out), aber ich würde vermuten, dass Sie std::map::find()Ihre Zeit nicht verschwenden möchten , wenn Sie erst einmal den Dreh raus haben.

Auch dein Code ist etwas falsch:

m.find('2');durchsucht die Karte nach einem Schlüsselwert '2'. IIRC Der C ++ - Compiler konvertiert implizit '2' in ein int, was zu dem numerischen Wert für den ASCII-Code für '2' führt, der nicht Ihren Wünschen entspricht.

Da Ihr Schlüsseltyp in diesem Beispiel lautet int, möchten Sie wie folgt suchen:m.find(2);


7
Wie? findzeigt Absicht weitaus besser als counttut. Darüber hinaus wird countder Artikel nicht zurückgegeben. Wenn Sie die Frage des OP lesen, möchte er die Existenz überprüfen und das Element zurückgeben. findtut das. countnicht.
Alan

if (m.count (key)) b3 = m [key]; // oder was auch immer
Pconnell

62
Ich war schon immer neugierig, welche Art von Unkraut die Leute rauchte, die die gesamte stl-API entworfen haben.
Falle

2
Alan Ich muss @dynamic in diesem Punkt zustimmen. Einen Iterator definieren und dann mit end vergleichen zu müssen, ist keine natürliche Art zu sagen, dass etwas nicht existiert. Es scheint mir viel einfacher zu sein zu sagen, dass ein bestimmtes Element mindestens einmal in dieser Karte vorkommt. Welches ist, was zählt.
PiersyP

1
@Claudiu C ++ 20 fügt genau das hinzu.
Pooya13

330

Solange die Karte keine Multimap ist, besteht eine der elegantesten Möglichkeiten darin, die Zählmethode zu verwenden

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

Die Anzahl wäre 1, wenn das Element tatsächlich in der Karte vorhanden ist.


22
Überprüft dies nicht alle Schlüssel, auch wenn es bereits einen gefunden hat? Das kann schnell teuer werden ...
mmdanziger

35
Es wird nur mehr als ein Schlüssel gezählt, wenn es auf einer Multimap verwendet wird.
Andrew Prock

14
@mmdanziger Nein, es wird nicht teuer: cplusplus.com/reference/map/map/count Count ist logarithmisch groß.
Jgyou

28
Der Schlüssel existiert und was dann? An diesem Punkt möchten Sie normalerweise den Wert dafür erhalten und für eine andere Suche bezahlen (z operator[]. B. mit ). findgibt Ihnen die .NET- TryGetValueSemantik, die fast immer das ist, was Sie (und speziell das OP) wollen.
Ohad Schneider

2
@serine verstanden. Beachten Sie, dass sich das Verhalten unterscheidet, falls der Schlüssel in der Version fehlt, da map [key] einen neu standardmäßig erstellten Elementwert zurückgibt.
Ohad Schneider

53

Es existiert bereits mit find nur nicht in genau dieser Syntax.

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

Wenn Sie auf den vorhandenen Wert zugreifen möchten, haben Sie folgende Möglichkeiten:

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

Mit C ++ 0x und Auto ist die Syntax einfacher:

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

Ich empfehle Ihnen, sich daran zu gewöhnen, anstatt zu versuchen, einen neuen Mechanismus zu finden, um ihn zu vereinfachen. Möglicherweise können Sie ein wenig Code reduzieren, aber berücksichtigen Sie die Kosten dafür. Jetzt haben Sie eine neue Funktion eingeführt, die mit C ++ vertraute Personen nicht erkennen können.

Wenn Sie dies trotz dieser Warnungen trotzdem implementieren möchten, dann:

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

Ich habe gerade bemerkt, dass wir mit C ++ 20 haben werden

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

Dies gibt true zurück, wenn map ein Element mit key enthält key.


Endlich eine Antwort, die über diese Funktion spricht! (C ++ 20)
Samuel Rasquinha

Endlich ? Danke, aber es ist fast 2 Jahre alt! ;-)
kebs

7

amap.findkehrt zurück, amap::endwenn es nicht findet, wonach Sie suchen - Sie sollten das überprüfen.


4

Überprüfen Sie den Rückgabewert von findgegen end.

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

1

Sie können Ihre getValue-Funktion mit dem folgenden Code erstellen:

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

Ich glaube, Zeile 6 sollte seinout = foundIter->second
Dithermaster

Ich habe out = foundIter->secondout = *foundIter
Kips

1

Um einige der anderen Antworten kurz zusammenzufassen:

Wenn Sie C ++ 20 noch nicht verwenden, können Sie Ihre eigene mapContainsKeyFunktion schreiben :

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

Wenn Sie viele Überladungen für mapvs unordered_mapund verschiedene Schlüssel- und Werttypen vermeiden möchten , können Sie dies zu einer templateFunktion machen.

Wenn Sie C++ 20oder später verwenden, gibt es eine integrierte containsFunktion:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

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

-1

Wenn Sie feststellen möchten, ob ein Schlüssel in der Karte vorhanden ist oder nicht, können Sie die Elementfunktion find () oder count () der Karte verwenden. Die hier im Beispiel verwendete Suchfunktion gibt den Iterator ansonsten an element oder map :: end zurück. Im Falle einer Zählung gibt die Zählung 1 zurück, wenn sie gefunden wird, andernfalls gibt sie Null zurück (oder auf andere Weise).

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 kann für die richtige Lösung verwendet werden. Die folgende Lösung ist keine sehr gute Option, kann jedoch in einigen Fällen nützlich sein, in denen der Benutzer bei der Initialisierung einen Standardwert wie 0 oder NULL zuweist und prüfen möchte, ob der Wert geändert wurde.

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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.