c++ - from - remove_if equivalente para std:: map
erase space from string c++ (11)
erase_if para std :: map (y otros contenedores)
Uso la siguiente plantilla para esto mismo.
namespace stuff {
template< typename ContainerT, typename PredicateT >
void erase_if( ContainerT& items, const PredicateT& predicate ) {
for( auto it = items.begin(); it != items.end(); ) {
if( predicate(*it) ) it = items.erase(it);
else ++it;
}
};
}
Esto no devolverá nada, pero eliminará los elementos del std :: map.
Ejemplo de uso:
// ''container'' could be a std::map
// ''item_type'' is what you might store in your container
using stuff::erase_if;
erase_if(container, []( item_type& item ) {
return /* insert appropriate test */;
});
Segundo ejemplo (le permite pasar un valor de prueba):
// ''test_value'' is value that you might inject into your predicate.
// ''property'' is just used to provide a stand-in test
using stuff::erase_if;
int test_value = 4; // or use whatever appropriate type and value
erase_if(container, [&test_value]( item_type& item ) {
return item.property < test_value; // or whatever appropriate test
});
Estaba tratando de borrar una gama de elementos del mapa en función de una condición particular. ¿Cómo lo hago usando algoritmos STL?
Inicialmente pensé en usar remove_if
pero no es posible, ya que remove_if no funciona para el contenedor asociativo.
¿Hay algún algoritmo equivalente "remove_if" que funcione para el mapa?
Como una opción simple, pensé en recorrer el mapa y borrar. ¿Pero está pasando por el mapa y borrando una opción segura? (Ya que los iteradores no son válidos después del borrado)
Usé el siguiente ejemplo:
bool predicate(const std::pair<int,std::string>& x)
{
return x.first > 2;
}
int main(void)
{
std::map<int, std::string> aMap;
aMap[2] = "two";
aMap[3] = "three";
aMap[4] = "four";
aMap[5] = "five";
aMap[6] = "six";
// does not work, an error
// std::remove_if(aMap.begin(), aMap.end(), predicate);
std::map<int, std::string>::iterator iter = aMap.begin();
std::map<int, std::string>::iterator endIter = aMap.end();
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
return 0;
}
Ahora, std::experimental::erase_if
está disponible en el encabezado <experimental/map>
.
Ver: http://en.cppreference.com/w/cpp/experimental/map/erase_if
Basado en la respuesta de Iron Savior Para aquellos que les gustaría proporcionar un rango más a lo largo de las líneas de iteradores de toma de funciones estándar.
template< typename ContainerT, class _FwdIt, class _Pr >
void erase_if(ContainerT& items, _FwdIt it, _FwdIt _Last, _Pr _Pred) {
for (; it != _Last; ) {
if (_Pred(*it)) it = items.erase(it);
else ++it;
}
};
Es curioso si hay alguna manera de perder los elementos de ContainerT y obtenerlos del iterador.
Casi.
for(; iter != endIter; ) {
if (Some Condition) {
aMap.erase(iter++);
} else {
++iter;
}
}
Lo que tenía originalmente aumentaría el iterador dos veces si borrase un elemento de él; podría saltear elementos que debían borrarse.
Este es un algoritmo común que he visto usado y documentado en muchos lugares.
[EDITAR] Tiene razón en que los iteradores se invalidan después de un borrado, pero solo los iteradores que hacen referencia al elemento que se borra, otros iteradores siguen siendo válidos. De ahí que use iter ++ en la llamada a erase ().
De las notas inferiores de:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
un contenedor asociativo de pares no puede proporcionar iteradores mutables (como se define en los requisitos Trivial Iterator), porque el tipo de valor de un iterador mutable debe ser asignable y el par no es asignable. Sin embargo, un contenedor asociativo de pares puede proporcionar iteradores que no son completamente constantes: iteradores tales que la expresión (* i) .segundo = d es válida.
El código original tiene solo un problema:
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
Aquí el iter
se incrementa una vez en el ciclo for y otro tiempo en el borrar, que probablemente terminará en un ciclo infinito.
En mi humilde opinión no hay remove_if()
equivalente.
No puedes reordenar un mapa.
Por remove_if()
tanto, remove_if()
no puede poner sus pares de interés al final sobre los que puede llamar a erase()
.
Obtuve esta documentación de la excelente referencia de SGI STL :
El mapa tiene la propiedad importante de que insertar un nuevo elemento en un mapa no invalida los iteradores que apuntan a los elementos existentes. Borrar un elemento de un mapa tampoco invalida ningún iterador, excepto, por supuesto, para los iteradores que realmente apuntan al elemento que se está borrando.
Por lo tanto, el iterador que tiene apuntando al elemento que se borrará será, por supuesto, invalidado. Haz algo como esto:
if (some condition)
{
iterator here=iter++;
aMap.erase(here)
}
Si desea borrar todos los elementos con una clave mayor que 2, entonces la mejor manera es
map.erase(map.upper_bound(2), map.end());
Sin embargo, solo funciona para rangos, no para ningún predicado.
primero
El mapa tiene la propiedad importante de que insertar un nuevo elemento en un mapa no invalida los iteradores que apuntan a los elementos existentes. Borrar un elemento de un mapa tampoco invalida ningún iterador, excepto, por supuesto, para los iteradores que realmente apuntan al elemento que se está borrando.
En segundo lugar, el siguiente código es bueno
for(; iter != endIter; )
{
if(Some Condition)
{
aMap.erase(iter++);
}
else
{
++iter;
}
}
Cuando se llama a una función, los parámetros se evalúan antes de la llamada a esa función.
Entonces, cuando se evalúa iter ++ antes de la llamada a borrar, el operador ++ del iterador devolverá el elemento actual y apuntará al siguiente elemento después de la llamada.
La respuesta de Steve Folly me hace sentir más eficiente.
Aquí hay otra solución fácil pero menos eficiente :
La solución utiliza remove_copy_if
para copiar los valores que queremos en un nuevo contenedor, luego intercambia los contenidos del contenedor original con los del nuevo:
std::map<int, std::string> aMap;
...
//Temporary map to hold the unremoved elements
std::map<int, std::string> aTempMap;
//copy unremoved values from aMap to aTempMap
std::remove_copy_if(aMap.begin(), aMap.end(),
inserter(aTempMap, aTempMap.end()),
predicate);
//Swap the contents of aMap and aTempMap
aMap.swap(aTempMap);