Визначте, чи карта містить значення для ключа?


256

Який найкращий спосіб визначити, чи карта STL містить значення для заданого ключа?

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

}

Досліджуючи це на відладчику, схоже, iterце лише дані про сміття.

Якщо я відменю цей рядок:

Bar b2 = m[2]

Налагоджувач показує, що b2є {i = 0}. (Я здогадуюсь, це означає, що використання невизначеного індексу поверне структуру з усіма порожніми / неініціалізованими значеннями?)

Жоден із цих методів не є таким чудовим. Мені дуже хотілося б такий інтерфейс:

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

Чи існує щось у цьому напрямку?


Відповіді:


274

Чи існує щось у цьому напрямку?

Ні. Клас stl map ви використовуєте ::find()для пошуку на карті та порівняння повернутого ітератораstd::map::end()

так

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

Очевидно, що ви можете написати свій власний getValue()розпорядок, якщо хочете (також на C ++, немає ніяких причин для використання out), але я б підозрював, що, як тільки ви отримаєте звичку використання, std::map::find()ви не захочете витрачати свій час.

Також ваш код трохи неправильний:

m.find('2');буде шукати на карті ключове значення, яке є '2'. IIRC компілятор C ++ неявно перетворить '2' в int, що призведе до числового значення коду ASCII для '2', що не є тим, що потрібно.

Оскільки ваш ключовий тип в цьому прикладі, intви хочете здійснити пошук таким чином:m.find(2);


7
Як так? findвказує на наміри набагато краще, ніж є count. Більше countтого, товар не повертається. Якщо ви читаєте питання ОП, він хоче перевірити наявність і повернути елемент. findробить це. countне.
Алан

if (m.count (key)) b3 = m [ключ]; // або що завгодно
pconnell

61
Мені завжди було цікаво, який саме бур’ян курять люди, які розробили цілий stl API.
Пастка

2
Алан. Я повинен погодитися з @dynamic щодо цього, потрібно визначити ітератор, а потім порівняти його з кінцем - це не природний спосіб сказати, що чогось не існує. Мені здається набагато простіше сказати, що певний елемент з’являється хоча б раз на цій карті. Це те, що робить рахунок.
PiersyP

1
@Claudiu C ++ 20 додає саме це.
pooya13

330

Поки карта не є багатомальовою, одним із найелегантніших способів було б скористатися методом підрахунку

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

Кількість буде 1, якщо елемент дійсно присутній на карті.


22
Не вдасться це перевірити всі клавіші, навіть якщо він вже знайшов його? Це може швидко дорожче ...
mmdanziger

35
Він буде нараховувати більше, ніж один ключ, якщо він використовується в мультимапі.
Ендрю Прок

14
@mmdanziger Ні, це не буде дорого: cplusplus.com/reference/map/map/count Кількість має логарифмічний розмір.
jgyou

28
Ключ існує, а далі що? У цей момент ви, як правило, хочете отримати цінність для нього, заплативши за інший пошук (наприклад, користуючись operator[]). findдає вам TryGetValueсемантику .NET , яка майже завжди є тим, чого ви хочете (і конкретно ОП).
Охад Шнайдер

2
@serine Зрозумів. Зауважте, що у випадку відсутності ключа у випуску поведінка буде іншою, оскільки map [key] поверне щойно створене за замовчуванням значення елемента.
Охад Шнайдер

53

Він уже існує з знахідкою не тільки в тому точному синтаксисі.

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

Якщо ви хочете отримати доступ до значення, якщо воно існує, ви можете зробити:

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

З C ++ 0x та auto, синтаксис простіший:

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

Я рекомендую скоріше звикнути до цього, ніж намагатися придумати новий механізм для його спрощення. Можливо, ви зможете скоротити трохи коду, але врахуйте вартість цього. Тепер ви запровадили нову функцію, яку люди, знайомі з C ++, не зможуть розпізнати.

Якщо ви хочете все-таки реалізувати це, незважаючи на ці попередження, то:

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

Я щойно помітив, що з C ++ 20 у нас буде

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

Це повернеться істинним, якщо карта містить елемент з ключем key.


Нарешті відповідь, що говорить про цю функцію! (C ++ 20)
Самуель Раскінья

Нарешті? Дякую, але це майже 2 роки! ;-)
кеби

7

amap.findповертається, amap::endколи він не знаходить того, що ви шукаєте - ви повинні це перевірити.


4

Перевірте значення повернення findпроти end.

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

1

Ви можете створити свою функцію getValue за допомогою наступного коду:

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

Я вважаю, що рядок 6 повинен бутиout = foundIter->second
Дітермайстер

Я зафіксував відповідь Кіпа, щоб правильно показати, out = foundIter->secondа неout = *foundIter
netjeff

1

Щоб коротко узагальнити деякі інші відповіді:

Якщо ви ще не використовуєте C ++ 20, ви можете написати власну mapContainsKeyфункцію:

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

Якщо ви хочете уникнути багатьох перевантажень для mapvs unordered_mapта різних типів ключів і значень, ви можете зробити це templateфункцією.

Якщо ви використовуєте C++ 20або пізніше, буде вбудована containsфункція:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

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

-1

Якщо ви хочете визначити, чи є ключ у карті, чи ні, ви можете скористатися функцією "find () або count () члена карти. Функція find, яка тут використовується в прикладі, повертає ітератор до елемента або map :: end в іншому випадку. У разі підрахунку підрахунок повертає 1, якщо його знайдено, інакше він повертає нуль (або інакше).

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

Для правильного рішення можна використовувати мультиіндекс. Наступне рішення - не найкращий варіант, але може бути корисним у кількох випадках, коли користувач при ініціалізації призначає значення за замовчуванням, як 0 або NULL, і хоче перевірити, чи змінено значення.

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.