Determinar se o mapa contém um valor para uma chave?

256

Qual é a melhor maneira de determinar se um mapa STL contém um valor para uma determinada chave?

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

}

Examinando isso em um depurador, parece iterapenas dados de lixo.

Se eu descomentar esta linha:

Bar b2 = m[2]

As mostras do depurador que b2é {i = 0}. (Acho que isso significa que o uso de um índice indefinido retornará uma estrutura com todos os valores vazios / não inicializados?)

Nenhum desses métodos é tão bom. O que eu realmente gostaria é de uma interface como esta:

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

Existe algo nesse sentido?

Nick Heiner
fonte

Respostas:

274

Existe algo nesse sentido?

Não. Com a classe stl map, você usa ::find()para pesquisar o mapa e comparar o iterador retornado parastd::map::end()

tão

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

Obviamente, você pode escrever sua própria getValue()rotina, se quiser (também em C ++, não há motivo para usá-lo out), mas eu suspeitaria que, depois que você pegar o jeito std::map::find(), não vai querer perder tempo.

Além disso, seu código está um pouco errado:

m.find('2'); procurará no mapa um valor-chave que seja '2' . IIRC, o compilador C ++ converterá implicitamente '2' em um int, o que resulta no valor numérico do código ASCII para '2', que não é o que você deseja.

Como seu tipo de chave neste exemplo é intvocê deseja pesquisar assim:m.find(2);

Alan
fonte
7
Como assim? findindica intenção muito melhor do que countfaz. Mais, countnão retorna o item. Se você ler a pergunta do OP, ele deseja verificar a existência e retornar o elemento. findfaz isso. countnão.
23713 Alan Alan
if (m.count (chave)) b3 = m [chave]; // ou o que for #
pconnell
64
Sempre fiquei curioso para saber que tipo de maconha estava fumando as pessoas que criaram toda a API stl.
Armadilha
2
Alan Eu tenho que concordar com @dynamic neste, ter que definir um iterador e depois compará-lo com o final não é uma maneira natural de dizer que algo não existe. Parece-me muito mais direto dizer que um determinado elemento aparece pelo menos uma vez neste mapa. Qual é o que conta faz.
PiersyP
1
O @Claudiu C ++ 20 adiciona exatamente isso.
precisa saber é o seguinte
330

Desde que o mapa não seja multimap, uma das maneiras mais elegantes seria usar o método count

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

A contagem seria 1 se o elemento estiver realmente presente no mapa.

pconnell
fonte
22
Isso não verifica todas as chaves, mesmo que já tenha encontrado uma? Isso pode ficar caro rapidamente ...
mmdanziger 24/10/12
35
Contará apenas mais de uma chave se usada em um multimapa.
Andrew Prock
14
@mmdanziger Não, não será caro: cplusplus.com/reference/map/map/count A contagem é de tamanho logarítmico.
jgyou
28
A chave existe e depois o que? Nesse ponto, você normalmente deseja obter o valor, pagando por outra pesquisa (por exemplo, usando operator[]). findfornece a TryGetValuesemântica do .NET , que quase sempre é o que você (e especificamente o OP) deseja.
Ohad Schneider
2
@serine Entendido. Observe que, caso a chave esteja ausente no release, o comportamento será diferente, pois map [key] retornará um valor de elemento recém-construído por padrão.
Ohad Schneider
53

Ele já existe com find e não apenas nessa sintaxe exata.

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

Se você deseja acessar o valor, se existir, você pode:

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

Com C ++ 0x e automático, a sintaxe é mais simples:

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

Eu recomendo que você se acostume a isso, em vez de tentar criar um novo mecanismo para simplificá-lo. Você pode reduzir um pouco de código, mas considere o custo de fazer isso. Agora você introduziu uma nova função que as pessoas familiarizadas com C ++ não poderão reconhecer.

Se você deseja implementar isso de qualquer maneira, apesar desses avisos, então:

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
fonte
38

Acabei de notar que, com o C ++ 20 , teremos

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

Isso retornará true se o mapa contiver um elemento com a chave key.

kebs
fonte
Finalmente, uma resposta que fala sobre essa função! (C ++ 20)
Samuel Rasquinha
Finalmente ? Obrigado, mas tem quase 2 anos! ;-)
kebs
7

amap.findretorna amap::endquando não encontra o que você está procurando - você deve verificar isso.

Alex Martelli
fonte
4

Verifique o valor de retorno de findcontra end.

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

Você pode criar sua função getValue com o seguinte código:

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;
}
Kip Streithorst
fonte
Eu acredito que a linha 6 deve serout = foundIter->second
Dithermaster
Fixei a resposta de Kip para corretamente mostrar out = foundIter->secondao invés deout = *foundIter
netjeff
1

Para resumir sucintamente algumas das outras respostas:

Se você ainda não estiver usando o C ++ 20, poderá escrever sua própria mapContainsKeyfunção:

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

Se você deseja evitar muitas sobrecargas para mapvs unordered_mape diferentes tipos de chave e valor, pode fazer disso uma templatefunção.

Se você estiver usando C++ 20ou mais tarde, haverá uma containsfunção interna:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

if (myMap.contains(key))
{
  // stuff here
}
cdahms
fonte
-1

Se você deseja determinar se uma chave existe ou não no mapa, você pode usar a função de membro find () ou count () do mapa. A função find que é usada aqui no exemplo retorna o iterador ao elemento ou map :: end, caso contrário. Em caso de contagem, a contagem retornará 1 se for encontrada, caso contrário, retornará zero (ou não).

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;
}
Prashant Shubham
fonte
-1

O multi-índice de impulso pode ser usado para a solução adequada. A solução a seguir não é a melhor opção, mas pode ser útil em alguns casos em que o usuário está atribuindo valor padrão como 0 ou NULL na inicialização e deseja verificar se o valor foi modificado.

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
fonte