Ustal, czy mapa zawiera wartość klucza?

256

Jaki jest najlepszy sposób ustalenia, czy mapa STL zawiera wartość dla danego klucza?

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

}

Analizując to w debugerze, wygląda na to, że iterto tylko śmieciowe dane.

Jeśli odkomentuję tę linię:

Bar b2 = m[2]

Debuger pokazuje, że tak b2jest {i = 0}. (Zgaduję, że oznacza to, że użycie niezdefiniowanego indeksu zwróci strukturę ze wszystkimi pustymi / niezainicjowanymi wartościami?)

Żadna z tych metod nie jest tak świetna. Naprawdę chciałbym taki interfejs:

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

Czy istnieje coś podobnego?

Nick Heiner
źródło

Odpowiedzi:

274

Czy istnieje coś podobnego?

Nie. Za pomocą klasy mapy stl możesz ::find()przeszukiwać mapę i porównywać z nią zwrócony iteratorstd::map::end()

więc

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

Oczywiście możesz napisać własną getValue()procedurę, jeśli chcesz (także w C ++, nie ma powodu, aby używać out), ale podejrzewam, że kiedy już zaczniesz używać std::map::find(), nie będziesz chciał tracić czasu.

Również twój kod jest nieco niepoprawny:

m.find('2');przeszuka mapę w poszukiwaniu wartości klucza, która jest '2'. IIRC kompilator C ++ niejawnie konwertuje „2” na int, co skutkuje wartością liczbową kodu ASCII dla „2”, co nie jest tym, czego chcesz.

Ponieważ kluczem w tym przykładzie jest intwyszukiwanie w następujący sposób:m.find(2);

Alan
źródło
7
Jak to? findwskazuje na zamiar znacznie lepiej niż countrobi. Co więcej, countnie zwraca przedmiotu. Jeśli przeczytasz pytanie PO, chce sprawdzić, czy istnieje, i zwrócić element. findrobi to. countnie.
Alan
if (m.count (key)) b3 = m [key]; // cokolwiek
pconnell
61
Zawsze byłem ciekawy, jakie trawki palą ludzie, którzy zaprojektowali cały API stl.
Pułapka
2
Alan Muszę się z tym zgodzić z @dynamicznym, konieczność zdefiniowania iteratora, a następnie porównania go z końcem nie jest naturalnym sposobem stwierdzenia, że ​​coś nie istnieje. Wydaje mi się o wiele łatwiej powiedzieć, że jakiś element pojawia się co najmniej raz na tej mapie. Co robi liczyć.
PiersyP,
1
@Claudiu C ++ 20 dodaje właśnie to.
pooya13
330

Tak długo, jak mapa nie jest mapą wielokrotną, jednym z najbardziej eleganckich sposobów byłoby użycie metody liczenia

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

Liczba będzie wynosić 1, jeśli element rzeczywiście jest obecny na mapie.

pconnell
źródło
22
Czy to nie sprawdzi wszystkich kluczy, nawet jeśli już je znalazł? To może stać się drogie szybko ...
mmdanziger
35
Będzie liczony tylko więcej niż jeden klucz, jeśli zostanie użyty na mapie.
Andrew Prock,
14
@mmdanziger Nie, to nie będzie drogie: cplusplus.com/reference/map/map/count Liczba jest logarytmiczna.
jgyou
28
Klucz istnieje, a co potem? W tym momencie zwykle chcesz uzyskać wartość, płacąc za kolejne wyszukiwanie (np. Za pomocą operator[]). finddaje TryGetValuesemantykę .NET , która prawie zawsze jest tym, czego chcesz (a konkretnie OP).
Ohad Schneider
2
@serine Zrozumiał. Zauważ, że w przypadku braku klucza w wydaniu zachowanie będzie inne, ponieważ map [klucz] zwróci nowo skonstruowaną wartość elementu.
Ohad Schneider,
53

Już istnieje, ale nie tylko w tej dokładnej składni.

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

Jeśli chcesz uzyskać dostęp do wartości, jeśli istnieje, możesz:

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

W C ++ 0x i auto składnia jest prostsza:

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

Radzę raczej przyzwyczaić się do tego, niż próbować wymyślić nowy mechanizm, aby go uprościć. Być może uda ci się trochę skrócić kod, ale weź pod uwagę koszty. Teraz wprowadziłeś nową funkcję, której osoby znające C ++ nie będą w stanie rozpoznać.

Jeśli mimo to chcesz je wdrożyć, mimo to:

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;
}
śmierdzący472
źródło
38

Właśnie zauważyłem, że z C ++ 20 będziemy mieli

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

Zwróci to wartość true, jeśli mapa zawiera element z kluczem key.

kebs
źródło
Wreszcie odpowiedź, która mówi o tej funkcji! (C ++ 20)
Samuel Rasquinha
Wreszcie ? Dzięki, ale ma prawie 2 lata! ;-)
kebs
7

amap.findzwraca, amap::endgdy nie znajdzie tego, czego szukasz - musisz to sprawdzić.

Alex Martelli
źródło
4

Sprawdź wartość zwracaną findPrzeciw end.

map<int, Bar>::iterator it = m.find('2');
if ( m.end() != it ) { 
  // contains
  ...
}
JaredPar
źródło
1

Możesz utworzyć swoją funkcję getValue za pomocą następującego kodu:

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;
}
Kip Streithorst
źródło
Uważam, że linia 6 powinna byćout = foundIter->second
Dithermaster
Naprawiłem odpowiedź Kipa, aby wyświetlać poprawnie, out = foundIter->secondzamiastout = *foundIter
netjeff
1

Aby zwięźle streścić niektóre inne odpowiedzi:

Jeśli nie używasz jeszcze C ++ 20, możesz napisać własną mapContainsKeyfunkcję:

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

Jeśli chcesz uniknąć wielu przeciążeń dla mapvs unordered_mapi różnych typów kluczy i wartości, możesz włączyć tę templatefunkcję.

Jeśli używasz C++ 20lub później, będzie wbudowana containsfunkcja:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

if (myMap.contains(key))
{
  // stuff here
}
cdahms
źródło
-1

Jeśli chcesz ustalić, czy klucz znajduje się na mapie, możesz użyć funkcji członkowskiej find () lub count () mapy. Funkcja find, która jest użyta tutaj w tym przykładzie, zwraca iterator do elementu lub map :: end w przeciwnym razie. W przypadku liczenia liczba zwraca 1, jeśli zostanie znaleziona, w przeciwnym razie zwraca zero (lub w inny sposób).

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;
}
Prashant Shubham
źródło
-1

Boind multindex może być użyty do właściwego rozwiązania. Poniższe rozwiązanie nie jest najlepszą opcją, ale może być przydatne w kilku przypadkach, w których użytkownik przy inicjalizacji przypisuje wartość domyślną, np. 0 lub NULL, i chce sprawdzić, czy wartość została zmodyfikowana.

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;
}
użytkownik2761565
źródło