Jak sprawdzić, czy dany klucz istnieje w C ++ std :: map

451

Próbuję sprawdzić, czy dany klucz znajduje się na mapie i jakoś nie mogę tego zrobić:

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

więc jak mogę wydrukować zawartość p?

Nic nie możemy zrobić
źródło
std::pair<iterator,bool> insert( const value_type& value );Jaki bool zwraca? informuje, czy klucz jest już obecny, czy nie?
krithikaGopalakrisnan

Odpowiedzi:

692

Posługiwać się map::find

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

źródło
105
Jeśli chcesz tylko sprawdzić, czy istnieje jakiś klucz, prawdopodobnie wolisz użyćmap::count
tomsmeding
10
@tomsmeding W std :: map jest tylko jeden klucz. Zatem liczba będzie wynosić 0 lub 1. Czy jedno jest bardziej wydajne od drugiego?
goelakash
34
@goelakash prawie nie; po prostu countzwraca intchwilę, a findzwraca cały iterator. Zapisujesz budowę iteratora :) Oczywiście, jeśli później zamierzasz użyć wartości, jeśli istnieje, użyj funkcji znajdź i zapisz jej wynik.
tomsmeding
9
@tomsmeding Jeśli korzystasz z multimapy, musisz przejrzeć cały kontener. W takim przypadku find () może być szybszy.
Trevor Hickey,
11
Dla tych, którzy szukają prędkości: count i findsą prawie identyczni w prędkości, gdy korzystają z map, które wymagają unikalnych kluczy. (1) Jeśli nie potrzebujesz elementów do utrzymania określonej kolejności, użyj std :: unordered_map , który ma prawie stałe wyszukiwania i może być bardzo korzystny, gdy przechowujesz więcej niż kilka par. (2) Jeśli chcesz użyć tej wartości, jeśli istnieje, zapisz wynik :: find i użyj iteratora, aby zapobiec 2 wyszukiwaniom:auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
cdgraham 19.03.19
305

Aby sprawdzić, czy istnieje konkretny klucz na mapie, użyj countfunkcji członka na jeden z następujących sposobów:

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

Dokumentacja dla map::findmówi: „Inną funkcją członka, map::countmogą być stosowane po prostu sprawdzić, czy dany klucz istnieje.”

Dokumentacja dla map::countmówi: „Ponieważ wszystkie elementy w pojemniku mapy są unikalne, funkcja może zwrócić tylko 1 (jeśli element zostanie znaleziony) lub zero (inaczej).”

Aby pobrać wartość z mapy za pomocą klucza, o którym wiesz, że istnieje, użyj map :: at :

value = m.at(key)

W przeciwieństwie do map :: operator [] , map::atnie utworzy nowego klucza na mapie, jeśli określony klucz nie istnieje.

DavidRR
źródło
33
Jeśli zamierzasz wykonać obie operacje, sprawdź, czy istnieje, a następnie zrób coś z tym. Użyj findzamiast tego. secondAtrybut zwrócony przez iteracyjnej findmożna stosować pobrać wartość klucza. Jeśli używasz countwtedy atlub operator[]wykonujesz dwie operacje, kiedy mógłbyś użyć tylko jednej.
OdraEncoded
1
Nie musisz robić> 0, == 1 lub! = 0; dokładnie to sprawdza C ++ w instrukcji if (warunek! = 0), więc możesz po prostu użyćif(m.count(key))
jv110
6
@ jv110 Kompilator Microsoft C ++ wydaje ostrzeżenie, gdy napotka rzutowanie z intna bool. Chociaż istnieją inne kompilatory C ++, które nie wydają podobnego ostrzeżenia, wolę używać jawnego porównania, aby wyjaśnić intencję i zwiększyć czytelność. Zauważ, że inne języki, takie jak C #, zabraniają takiej niejawnej konwersji, aby zapobiec możliwości wprowadzenia subtelnych błędów programowania.
DavidRR,
jaka jest złożoność czasowa liczenia? Czy to tylko operacja O (1)?
Mazeryt
1
@Mazeryt Biorąc pod uwagę, że mówimy o klasie w standardowej bibliotece C ++, z pewnością bym tak założył. Aby uzyskać dyskusję na pytanie bez względu na język, zobacz Czy tabele skrótów naprawdę mogą być O (1)? .
DavidRR
47

C ++ 20 pozwala nam std::map::containsto zrobić.

#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";
    }
}
Denis Sablukov
źródło
35
Chyba to powiem: w końcu.
Erik Campobadal
2
O czasie .....
Ridhuvarshan
39

Możesz użyć .find():

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

if (i == m.end()) { /* Not found */ }
else { /* Found, i->first is f, i->second is ++-- */ }
Thomas Bonini
źródło
15
m.find == m.end() // not found 

Jeśli chcesz użyć innego interfejsu API, znajdź idź do 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";
aJ.
źródło
12

Myślę, że chcesz map::find. Jeśli m.find("f")jest równy m.end(), to klucz nie został znaleziony. W przeciwnym razie funkcja find zwraca iterator wskazujący na znaleziony element.

Błąd wynika z tego, że p.firstjest iteratorem, który nie działa przy wstawianiu strumienia. Zmień ostatnią linię na cout << (p.first)->first;. pjest parą iteratorów, p.firstjest iteratorem, p.first->firstjest ciągiem kluczy.

Mapa może mieć tylko jeden element dla danego klucza, więc equal_rangenie jest zbyt przydatna. Jest zdefiniowany dla mapy, ponieważ jest zdefiniowany dla wszystkich kontenerów asocjacyjnych, ale jest o wiele bardziej interesujący dla wielu map.

Steve Jessop
źródło
W rzeczywistości, ponieważ jest to para iteratorów mapy, powinna ona być „cout << p.first-> pierwsza;”
stefaanv
Naprawiłem swoją odpowiedź, dzięki. To właśnie otrzymuję za brak kompilacji mojego kodu. I masz rację (w skreślonym komentarzu) na temat sprawdzania ważności, ale ja tylko próbowałem wyjaśnić, dlaczego nie mógł wydrukować p. Pierwszego, i to nie dlatego, że jest nieważny - wiemy, że „f” zostanie znalezione. Ponieważ w ogóle nie zalecam równego zakresu, nie zamierzam wyświetlać w tym celu kodu sprawdzającego błędy.
Steve Jessop,
Wow, naprawdę skanujesz SO. Właśnie dodałem to dla kompletności, ponieważ twój punkt był jasny. Dodałem kontrolę poprawności do mojej poprzedniej odpowiedzi, ale twoja odpowiedź mnie biła, więc usunąłem ją, ponieważ i tak nie dodała tak dużo, jak wspomniałeś.
stefaanv
Tak, w ogóle to widziałem, ponieważ twój komentarz pojawił się, kiedy opublikowałem mój.
Steve Jessop,
12

C++17uprościłem to trochę bardziej dzięki If statement with initializer. W ten sposób możesz mieć swoje ciasto i zjeść je.

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..
}
WBuck
źródło
4
map<string, string> m;

sprawdź, czy klucz istnieje, czy nie, i zwraca liczbę wystąpień (0/1 na mapie):

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

sprawdź, czy klucz istnieje, czy nie, i zwróć iterator:

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

w twoim pytaniu błąd spowodowany złym operator<<przeciążeniem, ponieważ p.firstjest to map<string, string>, że nie można go wydrukować. Spróbuj tego:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}
hustljian
źródło
1
Masz literówkę. Zmień „cout” na „count”
Rivka
1
I ta literówka naprawdę może kogoś wyrzucić, ponieważ coutmoże oznaczać coś zupełnie innego niżcount
modulitos 10.10.14
4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Oczywiście, jeśli chcesz uzyskać bardziej wyszukany efekt, zawsze możesz utworzyć szablon funkcji, która również przyjęła funkcję znalezioną i funkcję nieodnalezioną, coś w stylu:

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

I użyj tego w ten sposób:

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

Minusem tego jest dobre imię, „find_and_execute” jest niezręczny i nie mogę wymyślić nic lepszego od głowy…

Jagnięcina
źródło
3

Zachowaj ostrożność przy porównywaniu wyniku wyszukiwania z końcem, jak dla mapy „m”, ponieważ wszystkie odpowiedzi zrobiono powyżej map :: iterator i = m.find („f”);

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

nie powinieneś próbować wykonywać żadnych operacji, takich jak drukowanie klucza lub wartości za pomocą iteratora i, jeśli jest ona równa m.end (), w przeciwnym razie doprowadzi to do błędu segmentacji.

niezwyciężony
źródło
0

Porównując kod std :: map :: find i std :: map :: count, powiedziałbym, że pierwszy może przynieść pewną przewagę wydajnościową:

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);
    }
Nadzieja
źródło
0

Wiem, że to pytanie ma już kilka dobrych odpowiedzi, ale myślę, że moje rozwiązanie warte jest podzielenia się.

Działa zarówno std::mapi std::vector<std::pair<T, U>>jest dostępny z 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;
    }
}
Orzechówka
źródło
-5

Jeśli chcesz porównać parę map, możesz użyć tej metody:

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

To przydatna technika.

EmreS
źródło
Jako początkujący w programowaniu w C ++ jestem naprawdę ciekawy, dlaczego ta odpowiedź została odrzucona. Dlaczego ta odpowiedź jest niepopularna?
gromit190,
3
@ gromit190, ponieważ używa całej innej struktury danych, aby sprawdzić, czy klucz istnieje, gdy std :: map ma już taką możliwość. Wymagałoby to również synchronizacji między dwiema strukturami danych, co jest zależnością, z którą nikt nie chce sobie poradzić.
Lambage
-5
map <int , char>::iterator itr;
    for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
    {
        if (itr->second == 'c')
        {
            cout<<itr->first<<endl;
        }
    }
Muhammad Ahmad Zafar
źródło
3
Opracuj swój kod. Fragment bez żadnego wyjaśnienia nie jest pomocny na dłuższą metę.
iBug