¿Determinar si el mapa contiene un valor para una clave?


256

¿Cuál es la mejor manera de determinar si un mapa STL contiene un valor para una clave determinada?

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

}

Al examinar esto en un depurador, parece que itersolo se trata de datos basura.

Si descomento esta línea:

Bar b2 = m[2]

Los espectáculos depurador que b2es {i = 0}. (Supongo que significa que usar un índice indefinido devolverá una estructura con todos los valores vacíos / sin inicializar).

Ninguno de estos métodos es tan bueno. Lo que realmente me gustaría es una interfaz como esta:

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

¿Existe algo en este sentido?


Respuestas:


274

¿Existe algo en este sentido?

No. Con la clase de mapa stl, se usa ::find()para buscar en el mapa y comparar el iterador devuelto constd::map::end()

entonces

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

Obviamente, puede escribir su propia getValue()rutina si lo desea (también en C ++, no hay razón para usar out), pero sospecho que una vez que aprenda a usarlo std::map::find(), no querrá perder su tiempo.

Además, su código es ligeramente incorrecto:

m.find('2');buscará en el mapa un valor clave que sea '2'. IIRC, el compilador de C ++ convertirá implícitamente '2' en un int, lo que da como resultado el valor numérico para el código ASCII para '2' que no es lo que desea.

Dado que su tipo de clave en este ejemplo es intque desea buscar así:m.find(2);


77
¿Cómo es eso? findindica intención mucho mejor que lo counthace. Más, countno devuelve el artículo. Si lees la pregunta del OP, él quiere verificar la existencia y devolver el elemento. findhace eso. countno.
Alan

if (m.count (clave)) b3 = m [clave]; // o lo que sea
pconnell

64
Siempre tuve curiosidad por saber qué tipo de hierba fumaban las personas que diseñaron toda la API stl.
Trampa del

2
Alan, tengo que estar de acuerdo con @dynamic en este caso, tener que definir un iterador y luego compararlo con end no es una forma natural de decir que algo no existe. Me parece mucho más sencillo decir que cierto elemento aparece al menos una vez en este mapa. Que es lo que cuenta.
PiersyP

1
@Claudiu C ++ 20 agrega justamente eso.
pooya13

330

Mientras el mapa no sea un mapa múltiple, una de las formas más elegantes sería usar el método de conteo

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

El recuento sería 1 si el elemento está realmente presente en el mapa.


22
¿No comprobará esto todas las claves incluso si ya ha encontrado una? Eso puede ser caro rápido ...
mmdanziger

35
Solo contará más de una clave si se usa en un mapa múltiple.
Andrew Prock

14
@mmdanziger No, no será costoso: cplusplus.com/reference/map/map/count Count es de tamaño logarítmico.
jgyou

28
La clave existe, ¿y luego qué? En ese punto, generalmente querrá obtener el valor para ello, pagando por otra búsqueda (por ejemplo, usando operator[]). findle ofrece la TryGetValuesemántica de .NET , que casi siempre es lo que usted (y específicamente el OP) desea.
Ohad Schneider

2
@serine Entendido. Tenga en cuenta que en caso de que falte la clave en el lanzamiento, el comportamiento será diferente, ya que map [key] devolverá un nuevo valor de elemento construido por defecto.
Ohad Schneider

53

Ya existe con find pero no en esa sintaxis exacta.

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

Si desea acceder al valor si existe, puede hacer:

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

Con C ++ 0x y auto, la sintaxis es más simple:

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

Te recomiendo que te acostumbres a él en lugar de intentar crear un nuevo mecanismo para simplificarlo. Es posible que pueda reducir un poco de código, pero considere el costo de hacerlo. Ahora ha introducido una nueva función que las personas familiarizadas con C ++ no podrán reconocer.

Si desea implementar esto de todos modos a pesar de estas advertencias, entonces:

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

39

Acabo de notar que con C ++ 20 , tendremos

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

Eso devolverá verdadero si el mapa contiene un elemento con clave key.


¡Finalmente una respuesta que habla de esta función! (C ++ 20)
Samuel Rasquinha

Finalmente ? ¡Gracias, pero tiene casi 2 años! ;-)
kebs

1
Esperemos que las personas se desplacen a esta solución y ya no usen las obsoletas. :)
Leśny Rumcajs

7

amap.findregresa amap::endcuando no encuentra lo que está buscando, se supone que debe verificarlo.


4

Verifique el valor de retorno de findcontra end.

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

1

Puede crear su función getValue con el siguiente 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;
}

Creo que la línea 6 debería serout = foundIter->second
Dithermaster

Me fijo respuesta de Kip para mostrar correctamente out = foundIter->seconden lugar deout = *foundIter
netjeff

1

Para resumir sucintamente algunas de las otras respuestas:

Si aún no está utilizando C ++ 20, puede escribir su propia mapContainsKeyfunción:

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

Si desea evitar muchas sobrecargas para mapvs unordered_mapy diferentes tipos de clave y valor, puede hacer que esto sea una templatefunción.

Si está utilizando C++ 20o posterior, habrá una containsfunción incorporada:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

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

-1

Si desea determinar si hay una clave en el mapa o no, puede usar la función miembro find () o count () del mapa. La función de búsqueda que se utiliza aquí, por ejemplo, devuelve el iterador a element o map :: end de lo contrario. En caso de conteo, el conteo devuelve 1 si se encuentra, de lo contrario, devuelve cero (o de lo contrario).

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 se puede utilizar para una solución adecuada. La siguiente solución no es la mejor opción, pero podría ser útil en algunos casos en los que el usuario asigna un valor predeterminado como 0 o NULL en la inicialización y desea verificar si el valor se ha 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;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.