Como descobrir se uma chave existe em um C ++ std :: map

450

Estou tentando verificar se uma determinada chave está em um mapa e de alguma forma não consigo:

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

então, como posso imprimir o que está em p?

Não há nada que possamos fazer
fonte
std::pair<iterator,bool> insert( const value_type& value );Qual é o bool que ele retorna? diz, se a chave já está presente ou não?
krithikaGopalakrisnan

Respostas:

691

Usar map::find

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

fonte
105
Se você quiser apenas para verificar se existe uma determinada chave, você provavelmente preferiria usarmap::count
tomsmeding
10
@tomsmeding Existe apenas uma chave em um std :: map. Portanto, a contagem será 0 ou 1. Um é mais eficiente que o outro?
goelakash
34
@goelakash dificilmente; é só que countretorna um inttempo e findretorna um iterador inteiro. Você salva a construção do iterador :) Obviamente, se posteriormente você usar o valor, se existir, use find e armazene o resultado.
tomsmeding
9
@tomsmeding Se você estiver usando um multimap, precisará procurar em todo o contêiner. Nesse caso, find () pode ser mais rápido.
Trevor Hickey
11
Para aqueles que procuram velocidade: count e findsão quase idênticos em velocidade ao usar mapas que exigem chaves únicas. (1) Se você não precisar dos elementos para manter uma ordem específica, use std :: unordered_map , que possui pesquisas quase constantes e pode ser muito benéfico ao armazenar mais do que alguns pares. (2) Se você desejar usar o valor, se existir, armazene o resultado de :: find e use o iterador para evitar 2 consultas:auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
cdgraham
305

Para verificar se existe uma chave específica no mapa, use a countfunção de membro de uma das seguintes maneiras:

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

A documentação para map::finddiz: "Outra função de membro,, map::countpode ser usada apenas para verificar se existe uma chave específica".

A documentação para map::countdiz: "Como todos os elementos em um contêiner de mapa são exclusivos, a função pode retornar apenas 1 (se o elemento for encontrado) ou zero (caso contrário)."

Para recuperar um valor do mapa por meio de uma chave que você sabe existir, use map :: at :

value = m.at(key)

Ao contrário de map :: operator [] , map::atnão criará uma nova chave no mapa se a chave especificada não existir.

DavidRR
fonte
33
Se você estiver fazendo as duas operações, verifique se elas existem e faça algo a respeito. Use em findvez disso. O secondatributo do iterador retornado por findpode ser usado para recuperar o valor da chave. Se você usar countentão atou operator[]estiver executando duas operações quando poderia ter usado apenas uma.
OdraEncoded
1
Você não precisa fazer> 0, == 1 ou! = 0; essa é a verificação exata C ++ faz em uma instrução if, assim você pode apenas usar (condição = 0!)if(m.count(key))
jv110
6
@ jv110 O compilador Microsoft C ++ emite um aviso quando encontra uma conversão de intpara bool. Embora existam outros compiladores C ++ que não emitem um aviso semelhante, prefiro usar uma comparação explícita para tornar clara a intenção e melhorar a legibilidade. Observe que outros idiomas, como o C #, proíbem uma conversão implícita para impedir a possibilidade de introduzir sutis erros de programação.
DavidRR
qual é a complexidade temporal da contagem? É apenas operação O (1)?
Mazeryt 14/05
1
@ Mazeryt Dado que estamos falando de uma classe na biblioteca padrão C ++, eu certamente assumiria isso. Para uma discussão independente de idioma de sua pergunta, consulte Tabelas de hash podem realmente ser O (1)? .
DavidRR
47

C ++ 20 nos dá std::map::containspara fazer isso.

#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";
    }
}
Denis Sablukov
fonte
34
Acho que vou dizer: finalmente.
Erik Campobadal
2
Sobre o tempo .....
Ridhuvarshan
39

Você pode usar .find():

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

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

Se você quiser usar outra API, encontre ir para 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";
aJ.
fonte
12

Eu acho que você quer map::find. Se m.find("f")for igual a m.end(), a chave não foi encontrada. Caso contrário, find retornará um iterador apontando para o elemento encontrado.

O erro ocorre porque p.firsté um iterador, que não funciona para inserção de fluxo. Mude sua última linha para cout << (p.first)->first;. pé um par de iteradores, p.firsté um iterador, p.first->firsté a string de chave.

Um mapa pode ter apenas um elemento para uma determinada chave, portanto, equal_rangenão é muito útil. É definido para o mapa, porque é definido para todos os contêineres associativos, mas é muito mais interessante para o multimap.

Steve Jessop
fonte
Na verdade, por ser um par de iteradores para um mapa, ele deve ser "cout << p.first-> first;"
21125 stefaanv
Eu corrigi minha resposta, obrigado. É isso que recebo por não compilar meu código. E você está certo (em um comentário excluído) sobre verificar a validade, mas eu estava apenas tentando explicar por que ele não conseguiu imprimir o p.f. primeiro, e não é por ser inválido - sabemos que "f" será encontrado. Como não recomendo o uso de equal_range, não pretendo mostrar o código de verificação de erros para isso.
21609 Steve Jessop
Uau, você está realmente digitalizando SO. Eu estava apenas adicionando-o para completar, porque seu ponto era claro. Eu adicionei a verificação de validade à minha resposta anterior, mas sua resposta me superou, então a excluí, porque ela não adicionou muita coisa, como você mencionou.
21449 stefaanv
Sim, eu só vi isso porque seu comentário apareceu quando eu publiquei o meu.
21609 Steve Jessop
12

C++17simplificou isso um pouco mais com um If statement with initializer. Desta forma, você pode ter o seu bolo e comê-lo também.

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..
}
WBuck
fonte
4
map<string, string> m;

verifique se a chave existe ou não e o número de retorno ocorre (0/1 no mapa):

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

verifique se a chave existe ou não e retorne o iterador:

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

na sua pergunta, o erro causado por uma operator<<sobrecarga incorreta , porque p.firsté map<string, string>, você não pode imprimi-lo. tente isto:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}
hustljian
fonte
1
Você tem um erro de digitação. Alterar "cout" para "count"
Rivka 07/07
1
E que erro de digitação pode realmente jogar alguém fora, como coutpode significar algo muito diferente do quecount
modulitos
4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Obviamente, se você quiser ficar mais sofisticado, sempre poderá modelar uma função que também tenha uma função encontrada e uma função não encontrada, algo como isto:

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

E use-o assim:

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

A desvantagem disso é criar um bom nome, "find_and_execute" é estranho e eu não consigo encontrar nada melhor do alto da minha cabeça ...

Lambage
fonte
3

Tenha cuidado ao comparar o resultado da busca com o final, como no mapa 'm', como todas as respostas foram feitas acima de map :: iterator i = m.find ("f");

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

você não deve tentar executar nenhuma operação, como imprimir a chave ou o valor com o iterador i, se for igual a m.end (), caso contrário, isso causará uma falha de segmentação.

invicto
fonte
0

Comparando o código de std :: map :: find e std :: map :: count, eu diria que o primeiro pode render alguma vantagem de desempenho:

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);
    }
Esperança
fonte
0

Sei que esta pergunta já tem boas respostas, mas acho que vale a pena compartilhar minha solução.

Ele funciona para ambos std::mapee std::vector<std::pair<T, U>>está disponível no 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;
    }
}
NutCracker
fonte
-5

Se você deseja comparar o par de mapa, pode usar este método:

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

Esta é uma técnica útil.

EmreS
fonte
Como iniciante na programação em C ++, estou realmente curioso para saber por que essa resposta foi reduzida. Por que essa resposta é impopular?
gromit190
3
@ gromit190 porque está usando toda uma estrutura de dados para ver se a chave existe quando std :: map já possui esse recurso. Isso também exigiria sincronização entre as duas estruturas de dados, que é uma dependência com a qual ninguém deseja lidar.
Lambage 12/07/19
-5
map <int , char>::iterator itr;
    for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
    {
        if (itr->second == 'c')
        {
            cout<<itr->first<<endl;
        }
    }
Muhammad Ahmad Zafar
fonte
3
Por favor, elabore seu código. Um trecho sem explicação não costuma ser útil a longo prazo.
iBug 15/12/19