¿Cómo puedo eliminar elementos de un std::map con un iterador?


Me gustaría recorrer un std::map y eliminar elementos en función de su contenido. Cuánto mejor sería hacer?

Author: Flow, 2011-01-05

3 answers

Si tiene un compilador compatible con C++11, aquí hay una manera fácil de hacer esto:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       itr = myMap.erase(itr);
    } else {
       ++itr;
    }
}

La idea es hacer avanzar el iterador desde el inicio del contenedor hasta el final, comprobando en cada paso si el par clave/valor actual debe eliminarse. Si es así, eliminamos el elemento iterado usando la función miembro erase, que luego devuelve un iterador al siguiente elemento en el mapa. De lo contrario, avanzamos el iterador hacia adelante normalmente.

Si no tiene un C++compatible con 11 compilador, o estás trabajando con una base de código más antigua, las cosas son un poco más complicadas. Antes de C++11, la función miembro erase no devolvería un iterador al siguiente elemento del mapa. Esto significaba que para eliminar un elemento mientras se itera, necesitarías usar una danza de tres partes:

  1. Copie el iterador actual.
  2. Avance el iterador actual al siguiente elemento.
  3. Llama a erase en la copia del iterador antiguo.

Esto se muestra aquí:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       std::map<K, V>::iterator toErase = itr;
       ++itr;
       myMap.erase(toErase);
    } else {
       ++itr;
    }
}

Este proceso era necesario porque si se acaba de llamar erase en el iterador, invalidarlo, lo que significa que operaciones como incremento y decremento conducirían a un comportamiento indefinido. El código anterior evita esto configurando una copia del iterador, avanzando itr para que esté en el siguiente elemento, y luego borrando la copia temporal del iterador.

Usando algún Truco Inteligente, es posible reducir este código a expensas de legibilidad. El siguiente patrón es común en código C++ anterior, pero no es necesario en C++11:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       myMap.erase(itr++);  // <--- Note the post-increment!
    } else {
       ++itr;
    }
}

El uso del operador post-incremento aquí es una forma inteligente de hacer una copia del iterador antiguo (recuerde que un operador postfix ++ devuelve una copia del valor del iterador original) mientras también avanza el iterador anterior.

 86
Author: templatetypedef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-27 19:13:08


Esta es una manera simple:

    int value_to_delete( 2 );
    for( std::map<int, int>::iterator i = mm.begin(); i != mm.end(); ) {
        if( i->second != value_to_delete ) {
            mm.erase( i++ ); // advance before iterator become invalid
        }
        else {
            ++i;
        }
    }
 7
Author: Chan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-05 03:34:04
for(MyMap::iterator it = mymap.begin(); it!=mymap.end(); ) {
  if(mycondition(it))
    it = mymap.erase(it);
  else
    it++;
}

Editar: parece que esto solo funciona en MSVC

Edit2: en c++0x esto también funciona para contenedores asociativos

 7
Author: Timo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-05 04:19:15