¿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?
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:
- Copie el iterador actual.
- Avance el iterador actual al siguiente elemento.
- 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.
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;
}
}
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
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