¿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?

Nick Heiner
fuente

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

Alan
fuente
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.

pconnell
fuente
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;
}
apestoso472
fuente
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.

kebs
fuente
¡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.

Alex Martelli
fuente
4

Verifique el valor de retorno de findcontra end.

map<int, Bar>::iterator it = m.find('2');
if ( m.end() != it ) { 
  // contains
  ...
}
JaredPar
fuente
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;
}
Kip Streithorst
fuente
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
}
cdahms
fuente
-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;
}
Prashant Shubham
fuente
-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;
}
usuario2761565
fuente