Как узнать, существует ли данный ключ в C ++ std :: map


451

Я пытаюсь проверить, есть ли данный ключ на карте и почему-то не могу это сделать:

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

так как я могу распечатать то, что в р?


std::pair<iterator,bool> insert( const value_type& value );Что за бул возвращается? это говорит, если ключ уже присутствует или нет?
krithikaGopalakrisnan

Ответы:


692

использование map::find

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

105
Если вы просто хотите проверить, существует ли определенный ключ, вы, вероятно, предпочли бы использоватьmap::count
tomsmeding

10
@tomsmeding В std :: map есть только один ключ. Таким образом, число будет либо 0, либо 1. Является ли один более эффективным, чем другой?
goelakash

34
@goelakash вряд ли; просто countвозвращает intвремя findвозвращает целый итератор. Вы сохраняете конструкцию итератора :) Очевидно, что если впоследствии вы собираетесь использовать значение, если оно существует, используйте find и сохраните его результат.
Томсминг

9
@tomsmeding Если вы используете мультикарту, вам придется просмотреть весь контейнер. В этом случае find () может быть быстрее.
Тревор Хики

11
Для тех, кто ищет скорость: count и findпочти идентичны по скорости при использовании карт, требующих уникальных ключей. (1) Если вам не нужны элементы для поддержания определенного порядка, используйте std :: unordered_map , который имеет почти постоянный поиск и может быть очень полезен при хранении нескольких пар. (2) Если вы хотите использовать значение, если оно существует, сохраните результат :: find и используйте итератор для предотвращения 2-х auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
поисков

305

Чтобы проверить, существует ли определенный ключ на карте, используйте countфункцию-член одним из следующих способов:

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

Документация для map::findговорит: «Еще одна функции, map::countможет быть использована только проверить , существует ли конкретный ключ.»

Документация для map::countговорит: «Поскольку все элементы в контейнере карт являются уникальными, то функция может возвращать только 1 (если элемент найден) или ноль ( в противном случае).»

Чтобы извлечь значение из карты через ключ, который, как вы знаете, существует, используйте map :: at :

value = m.at(key)

В отличие от map :: operator [] , map::atне будет создавать новый ключ на карте, если указанный ключ не существует.


33
Если вы собираетесь выполнить обе операции, проверьте, существует ли она, а затем сделайте что-нибудь с этим. Используйте findвместо этого. secondАтрибут итератора , возвращенного findможно использовать получить значение ключа. Если вы используете countто, atили operator[]вы выполняете две операции, когда вы могли бы использовать только одну.
OdraEncoded

1
Вам не нужно делать> 0, == 1 или! = 0; это именно то, что C ++ делает в операторе if (условие! = 0), так что вы можете просто использоватьif(m.count(key))
jv110

6
@ jv110 Компилятор Microsoft C ++ выдает предупреждение, когда встречает приведение из intв bool. Хотя есть другие компиляторы C ++, которые не выдают подобное предупреждение, я предпочитаю использовать явное сравнение, чтобы прояснить намерение и улучшить читаемость. Обратите внимание, что другие языки, такие как C #, запрещают такое неявное преобразование, чтобы исключить возможность незначительных ошибок программирования.
DavidRR

что такое временная сложность подсчета? Это просто O (1) операция?
Мазерит

1
@Mazeryt Учитывая, что мы говорим о классе в стандартной библиотеке C ++, я бы, конечно, предположил. Для обсуждения вашего вопроса, не зависящего от языка, см. Может ли хеш-таблица действительно быть O (1)? ,
DavidRR

47

C ++ 20 дает нам std::map::containsэто сделать.

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

35
Я думаю, я скажу это: наконец.
Эрик Кампобадал

2
О времени .....
Ridhuvarshan

39

Вы можете использовать .find():

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

if (i == m.end()) { /* Not found */ }
else { /* Found, i->first is f, i->second is ++-- */ }

15
m.find == m.end() // not found 

Если вы хотите использовать другой API, найдите go 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";

12

Я думаю, что вы хотите map::find. Если m.find("f")равно m.end(), то ключ не найден. В противном случае find возвращает итератор, указывающий на найденный элемент.

Ошибка в том, что p.firstэто итератор, который не работает для вставки потока. Измените свою последнюю строку на cout << (p.first)->first;. pявляется парой итераторов, p.firstявляется итератором, p.first->firstявляется ключевой строкой.

Карта может иметь только один элемент для данного ключа, поэтому equal_range она не очень полезна. Он определен для карты, потому что он определен для всех ассоциативных контейнеров, но гораздо интереснее для мультикарты.


На самом деле, поскольку это пара итераторов для карты, она должна быть "cout << p.first-> first;"
stefaanv

Я исправил свой ответ, спасибо. Вот что я получаю за то, что не компилировал мой код. И вы правы (в удаленном комментарии) в отношении проверки на достоверность, но я просто пытался объяснить, почему он не смог напечатать p.first, а не потому, что он недействителен - мы знаем, что «f» будет найдено. Так как я вообще не рекомендую использовать equal_range, я не собираюсь показывать код проверки ошибок для этого.
Стив Джессоп

Вау, ты действительно сканируешь ТАК. Я просто добавил это для полноты, потому что ваша точка зрения была ясна. Я добавил проверку достоверности к своему предыдущему ответу, но ваш ответ превзошел меня, поэтому я удалил его, потому что он все равно не так много добавил, как вы упомянули.
stefaanv

Да, я только видел это, потому что ваш комментарий появился, когда я опубликовал свой.
Стив Джессоп

12

C++17упростил это немного больше с If statement with initializer. Таким образом, вы можете получить свой торт и съесть его тоже.

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

4
map<string, string> m;

проверить ключ существует или нет, и возвращаемое число встречается (0/1 на карте):

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

проверить ключ существует или нет, и вернуть итератор:

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

в вашем вопросе, ошибка вызвана плохой operator<<перегрузкой, потому что p.firstэто map<string, string>, вы не можете распечатать его. попробуй это:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}

1
У вас есть опечатка. Измените «cout» на «count»
Ривка

1
И эта опечатка действительно может кого-то сбить с толку, поскольку это coutможет означать нечто совсем иное, чемcount
modulitos

4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Конечно, если вы хотите стать более любопытным, вы всегда можете шаблонизировать функцию, которая также принимает найденную функцию и не найденную функцию, что-то вроде этого:

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

И используйте это так:

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

Недостатком этого является создание хорошего названия, «find_and_execute» неудобно, и я не могу придумать ничего лучшего из головы ...


3

Будьте внимательны при сравнении результата поиска с концом, как для карты 'm', так как все ответы были сделаны выше map :: iterator i = m.find ("f");

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

Вы не должны пытаться выполнять какие-либо операции, такие как печать ключа или значения с помощью итератора i, если он равен m.end (), иначе это приведет к ошибке сегментации.


0

Сравнивая код std :: map :: find и std :: map :: count, я бы сказал, что первое может дать некоторое преимущество в производительности:

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

0

Я знаю, что на этот вопрос уже есть несколько хороших ответов, но я думаю, что мое решение стоит поделиться.

Он работает для обоих std::mapи std::vector<std::pair<T, U>>доступен из 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;
    }
}

-5

Если вы хотите сравнить пару карт, вы можете использовать этот метод:

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

Это полезная техника.


Как новичок в программировании на C ++, мне действительно любопытно, почему этот ответ опущен. Почему этот ответ непопулярен?
gromit190

3
@ gromit190, потому что он использует целую другую структуру данных, чтобы увидеть, существует ли ключ, когда std :: map уже имеет эту возможность. Это также потребовало бы синхронизации между двумя структурами данных, которая является зависимостью, с которой никто не хочет иметь дело.
Lambage

-5
map <int , char>::iterator itr;
    for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
    {
        if (itr->second == 'c')
        {
            cout<<itr->first<<endl;
        }
    }

3
Пожалуйста, уточните ваш код. Фрагмент без каких-либо объяснений, как правило, не помогает в долгосрочной перспективе.
iBug
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.