Як дізнатися, чи існує вказаний ключ на карті C ++ std ::


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

тож як я можу надрукувати те, що є в p?


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повертає цілий ітератор. Ви економите конструкцію ітератора :) Очевидно, якщо згодом ви збираєтесь використовувати значення, якщо воно існує, використовуйте пошук і зберігайте його результат.
tomsmeding

9
@tomsmeding Якщо ви використовуєте мультимапа, вам доведеться переглянути весь контейнер. У такому випадку пошук () може бути швидшим.
Тревор Хікі

11
Для тих, хто шукає швидкості: count і findмайже однаковий за швидкістю при використанні карт, для яких потрібні унікальні ключі. (1) Якщо вам не потрібні елементи для підтримки певного порядку, використовуйте std :: unordered_map , який має майже постійні перегляди і може бути дуже корисним при зберіганні більше кількох пар. (2) Якщо ви хочете використати значення, якщо воно існує, збережіть результат :: знайдіть і використовуйте ітератор, щоб запобігти 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, знайдіть іти 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 вже має цю можливість. Це також вимагає синхронізації між двома структурами даних, що є залежністю, з якою ніхто не хоче мати справу.
Ламбз

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