Cómo encontrar si una clave dada existe en un C ++ std :: map

450

Estoy tratando de verificar si una clave dada está en un mapa y de alguna manera no puedo hacerlo:

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

Entonces, ¿cómo puedo imprimir lo que está en p?

No hay nada que podemos hacer
fuente
std::pair<iterator,bool> insert( const value_type& value );¿Cuál es el bool que devuelve? ¿dice si la clave ya está presente o no?
krithikaGopalakrisnan

Respuestas:

691

Utilizar map::find

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

fuente
105
Si solo desea verificar si existe una clave determinada, probablemente map::count
prefiera
10
@tomsmeding Solo hay una clave en un std :: map. Entonces el conteo será 0 o 1. ¿Es uno más eficiente que el otro?
goelakash
34
@goelakash apenas; es solo que countdevuelve un inttiempo finddevuelve un iterador completo. Guarda la construcción del iterador :) Obviamente, si luego va a usar el valor si existe, use find y almacene su resultado.
tomsmeding
99
@tomsmeding Si está utilizando un mapa múltiple, tendría que revisar todo el contenedor. En cuyo caso, find () puede ser más rápido.
Trevor Hickey
11
Para aquellos que buscan velocidad: count y findson casi idénticos en velocidad cuando usan mapas que requieren claves únicas. (1) Si no necesita los elementos para mantener un orden específico, use std :: unordered_map , que tiene búsquedas casi constantes y puede ser muy beneficioso al almacenar más de unos pocos pares. (2) Si desea usar el valor si existe, almacene el resultado de :: find y use el iterador para evitar 2 búsquedas:auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
cdgraham
305

Para verificar si existe una clave particular en el mapa, use la countfunción miembro de una de las siguientes maneras:

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

La documentación de map::finddice: "Otra función miembro map::count, puede usarse para verificar si existe una clave en particular".

La documentación de map::countdice: "Debido a que todos los elementos en un contenedor de mapas son únicos, la función solo puede devolver 1 (si se encuentra el elemento) o cero (de lo contrario)".

Para recuperar un valor del mapa a través de una clave que sabe que existe, use map :: at :

value = m.at(key)

A diferencia de map :: operator [] , map::atno creará una nueva clave en el mapa si la clave especificada no existe.

DavidRR
fuente
33
Si va a hacer ambas operaciones, verifique si existe y luego haga algo al respecto. Usar en su findlugar. El secondatributo del iterador devuelto por findse puede utilizar para recuperar el valor de la clave. Si usa countentonces ato operator[]está realizando dos operaciones cuando podría haber usado solo una.
OdraEncoded
1
No necesita hacer> 0, == 1 o! = 0; esa es la comprobación exacta que C ++ hace en una declaración if (condición! = 0), por lo que puede usarif(m.count(key))
jv110
66
@ jv110 El compilador de Microsoft C ++ emite una advertencia cuando encuentra un envío de inta bool. Aunque hay otros compiladores de C ++ que no emiten una advertencia similar, prefiero usar una comparación explícita para aclarar la intención y mejorar la legibilidad. Tenga en cuenta que otros lenguajes como C # prohíben una conversión tan implícita para evitar la posibilidad de introducir errores de programación sutiles.
DavidRR
¿Cuál es la complejidad del tiempo de conteo? ¿Es solo una operación O (1)?
Mazeryt
1
@Mazeryt Dado que estamos hablando de una clase en la biblioteca estándar de C ++, ciertamente asumiría que sí. Para una discusión independiente de su pregunta, vea ¿Las tablas hash realmente pueden ser O (1)? .
DavidRR
47

C ++ 20 nos da std::map::containspara hacer eso.

#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
fuente
34
Supongo que lo diré: finalmente.
Erik Campobadal
2
Ya era
39

Puedes 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
fuente
15
m.find == m.end() // not found 

Si desea utilizar otra API, busque ir a 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.
fuente
12

Creo que quieres map::find. Si m.find("f")es igual a m.end(), entonces no se encontró la clave. De lo contrario, find devuelve un iterador que apunta al elemento encontrado.

El error se debe a que p.firstes un iterador, que no funciona para la inserción de secuencias. Cambia tu última línea a cout << (p.first)->first;. pes un par de iteradores, p.firstes un iterador, p.first->firstes la cadena clave.

Un mapa solo puede tener un elemento para una clave determinada, por equal_rangelo que no es muy útil. Está definido para el mapa, porque está definido para todos los contenedores asociativos, pero es mucho más interesante para los mapas múltiples.

Steve Jessop
fuente
En realidad, debido a que es un par de iteradores para un mapa, debería ser "cout << p.first-> first;"
stefaanv
He arreglado mi respuesta, gracias. Eso es lo que obtengo por no compilar mi código. Y tiene razón (en un comentario eliminado) sobre verificar la validez, pero solo estaba tratando de explicar por qué no pudo imprimir p.primero, y no es porque sea inválido, sabemos que se encontrará "f". Como no recomiendo usar equal_range en absoluto, no voy a mostrar un código de verificación de errores para eso.
Steve Jessop
Wow, realmente estás escaneando SO. Solo lo estaba agregando para completar, porque su punto era claro. Agregué la verificación de validez a mi respuesta anterior, pero su respuesta me superó, así que la eliminé, porque de todos modos no agregó mucho, como usted mencionó.
stefaanv
Sí, solo lo vi porque tu comentario apareció cuando publiqué el mío.
Steve Jessop
12

C++17simplificado esto un poco más con un If statement with initializer. De esta manera puedes tener tu pastel y comértelo también.

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
fuente
4
map<string, string> m;

la clave de verificación existe o no, y el número de retorno ocurre (0/1 en el mapa):

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

la clave de verificación existe o no, y devuelve el iterador:

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

en su pregunta, el error causado por una mala operator<<sobrecarga, porque p.firstes map<string, string>, no puede imprimirlo. prueba esto:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}
Hustljian
fuente
1
Tienes un error tipográfico. Cambiar "cout" a "contar"
Rivka
1
Y ese error tipográfico realmente puede sorprender a alguien, ya que coutpuede significar algo muy diferente acount
modulitos
4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Por supuesto, si desea ser más elegante, siempre puede crear una función que también tome una función encontrada y una función no encontrada, algo como esto:

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

Y úsalo así:

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

La desventaja de esto es que viene con un buen nombre, "find_and_execute" es incómodo y no se me ocurre nada mejor en la parte superior de mi cabeza ...

Lambage
fuente
3

Tenga cuidado al comparar el resultado de búsqueda con el final, como para el mapa 'm', ya que todas las respuestas se han hecho sobre map :: iterator i = m.find ("f");

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

no debe intentar realizar ninguna operación, como imprimir la clave o el valor con el iterador i si es igual a m.end () de lo contrario, provocará un error de segmentación.

Invictus
fuente
0

Comparando el código de std :: map :: find y std :: map :: count, diría que el primero puede proporcionar alguna ventaja de rendimiento:

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);
    }
Esperanza
fuente
0

Sé que esta pregunta ya tiene algunas buenas respuestas, pero creo que vale la pena compartir mi solución.

Funciona para ambos std::mapy std::vector<std::pair<T, U>>está disponible en 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;
    }
}
Cascanueces
fuente
-5

Si desea comparar un par de mapas, puede 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 es una técnica útil.

EmreS
fuente
Como principiante con la programación en C ++, tengo mucha curiosidad por qué esta respuesta es rechazada. ¿Por qué esta respuesta es impopular?
gromit190
3
@ gromit190 porque está usando otra estructura de datos para ver si la clave existe cuando std :: map ya tiene esta capacidad. Esto también requeriría la sincronización entre las dos estructuras de datos, que es una dependencia con la que nadie quiere lidiar.
Lambage
-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
fuente
3
Por favor, explique su código. Un fragmento sin ninguna explicación no suele ser útil a largo plazo.
iBug