Определить, содержит ли карта значение для ключа?

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 вы используете ::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 [key]; // или что-то еще
pconnell
64
Мне всегда было любопытно, что за курение курят люди, которые разработали весь API stl.
Ловушка
2
Алан Я должен согласиться с @dynamic в этом вопросе: необходимость определить итератор, а затем сравнить его с концом - не естественный способ сказать, что чего-то не существует. Мне кажется гораздо более простым сказать, что определенный элемент появляется хотя бы один раз на этой карте. Именно это и делает счет.
PiersyP
1
@Claudiu C ++ 20 добавляет только это.
pooya13
330

Пока карта не является мультикартой, одним из самых элегантных способов будет использование метода подсчета

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

Счет будет равен 1, если элемент действительно присутствует на карте.

pconnell
источник
22
Разве это не проверит все ключи, даже если он уже нашел один? Это может быстро стать дорогим ...
mmdanziger
35
Он будет рассчитывать более одного ключа, если он используется на мультикарте.
Андрей Прок
14
@mmdanziger Нет, это не будет дорого: cplusplus.com/reference/map/map/count Счетчик имеет логарифмический размер.
jgyou
28
Ключ существует, и что тогда? В этот момент вы обычно хотите получить значение для него, заплатив за другой поиск (например, используя operator[]). findдает вам TryGetValueсемантику .NET , которая почти всегда то, что вы (и особенно ОП) хотите.
Охад Шнайдер
2
@serine Понял. Обратите внимание, что в случае отсутствия ключа в релизе поведение будет другим, так как map [key] вернет новое значение элемента, созданное по умолчанию.
Охад Шнайдер
53

Он уже существует с find только не в том же синтаксисе.

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;
}
stinky472
источник
39

Я только что заметил, что с C ++ 20 у нас будет

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

Это вернет true, если карта содержит элемент с ключом key.

KEBS
источник
Наконец ответ, который говорит об этой функции! (C ++ 20)
Самуэль Раскинья
В заключение ? Спасибо, но ему почти 2 года! ;-)
кеб
1
Надеемся, что люди перейдут к этому решению и больше не будут использовать устаревшие. :)
Leśny Rumcajs
7

amap.findвозвращается, amap::endкогда не находит то, что вы ищете - вы должны это проверить.

Алекс Мартелли
источник
4

Проверьте возвращаемое значение findпротив end.

map<int, Bar>::iterator it = m.find('2');
if ( m.end() != it ) { 
  // contains
  ...
}
JaredPar
источник
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
Dithermaster
Я исправил ответ Кипа, чтобы правильно показывать, 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
}
cdahms
источник
-1

Если вы хотите определить, есть ли ключ на карте или нет, вы можете использовать функцию-член map (find) или count (). Функция find, которая используется здесь в примере, возвращает итератор element или 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

Boost multindex можно использовать для правильного решения. Следующее решение не очень лучший вариант, но может быть полезно в тех случаях, когда пользователь назначает значение по умолчанию, например 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;
}
user2761565
источник