En Cocoa, si quiero recorrer un NSMutableArray y eliminar varios objetos que cumplan un determinado criterio, ¿cuál es la mejor manera de hacerlo sin reiniciar el ciclo cada vez que elimino un objeto?
Gracias,
Editar: solo para aclarar: estaba buscando la mejor manera, por ejemplo, algo más elegante que actualizar manualmente el índice en el que estoy. Por ejemplo en C ++ puedo hacer;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
objective-c
cocoa
Andrew Grant
fuente
fuente
Respuestas:
Para mayor claridad, me gusta hacer un bucle inicial donde recopilo los elementos para eliminar. Luego los elimino. Aquí hay una muestra usando la sintaxis de Objective-C 2.0:
Entonces no hay dudas sobre si los índices se están actualizando correctamente u otros pequeños detalles de contabilidad.
Editado para agregar:
Se ha observado en otras respuestas que la formulación inversa debería ser más rápida. es decir, si itera por la matriz y compone una nueva matriz de objetos para guardar, en lugar de objetos para descartar. Eso puede ser cierto (aunque ¿qué pasa con la memoria y el costo de procesamiento de asignar una nueva matriz y descartar la anterior?) Pero incluso si es más rápida, puede no ser tan importante como lo sería para una implementación ingenua, porque NSArrays no se comporten como matrices "normales". Hablan la charla pero caminan un camino diferente. Vea un buen análisis aquí:
La formulación inversa puede ser más rápida, pero nunca he tenido que preocuparme si es así, porque la formulación anterior siempre ha sido lo suficientemente rápida para mis necesidades.
Para mí, el mensaje final es usar cualquier formulación que sea más clara para usted. Optimice solo si es necesario. Personalmente, considero que la formulación anterior es más clara, por eso la uso. Pero si la formulación inversa es más clara para usted, hágalo.
fuente
Una variante más. Entonces obtienes legibilidad y buen rendimiento:
fuente
removeObjectsAtIndexes
es el peor método para eliminar los objetos, ¿estás de acuerdo con eso? Estoy preguntando esto porque tu respuesta es demasiado vieja ahora. ¿Aún así es bueno elegir el mejor?enumerateObjectsUsingBlock:
te daría el incremento del índice gratis.Este es un problema muy simple. Simplemente iteras hacia atrás:
Este es un patrón muy común.
fuente
Algunas de las otras respuestas tendrían malos resultados en las matrices muy grandes, ya que los métodos como
removeObject:
eremoveObjectsInArray:
implican hacer una búsqueda lineal del receptor, que es una pérdida debido a que ya sabe dónde está el objeto. Además, cualquier llamada aremoveObjectAtIndex:
tendrá que copiar valores desde el índice hasta el final de la matriz en una ranura a la vez.Más eficiente sería lo siguiente:
Debido a que establecemos la capacidad de
itemsToKeep
, no perdemos el tiempo copiando valores durante un cambio de tamaño. No modificamos la matriz en su lugar, por lo que somos libres de usar la enumeración rápida. UsarsetArray:
para reemplazar el contenido dearray
conitemsToKeep
será eficiente. Dependiendo de su código, incluso podría reemplazar la última línea con:Por lo tanto, ni siquiera es necesario copiar valores, solo intercambiar un puntero.
fuente
Puede usar NSpredicate para eliminar elementos de su matriz mutable. Esto requiere no para bucles.
Por ejemplo, si tiene un NSMutableArray de nombres, puede crear un predicado como este:
La siguiente línea lo dejará con una matriz que contiene solo nombres que comienzan con b.
Si tiene problemas para crear los predicados que necesita, use este enlace de desarrollador de Apple .
fuente
Hice una prueba de rendimiento con 4 métodos diferentes. Cada prueba recorrió todos los elementos en una matriz de 100,000 elementos y eliminó cada quinto elemento. Los resultados no variaron mucho con / sin optimización. Estos se hicieron en un iPad 4:
(1)
removeObjectAtIndex:
- 271 ms(2)
removeObjectsAtIndexes:
- 1010 ms (porque construir el conjunto de índices lleva ~ 700 ms; de lo contrario, esto es básicamente lo mismo que llamar a removeObjectAtIndex: para cada elemento)(3)
removeObjects:
- 326 ms(4) hacer una nueva matriz con objetos que pasan la prueba - 17 ms
Por lo tanto, crear una nueva matriz es, con mucho, el más rápido. Los otros métodos son todos comparables, excepto que usar removeObjectsAtIndexes: será peor con más elementos para eliminar, debido al tiempo necesario para construir el conjunto de índices.
fuente
Utilice el ciclo de cuenta regresiva sobre los índices:
o haga una copia con los objetos que desea conservar.
En particular, no use un
for (id object in array)
bucle oNSEnumerator
.fuente
Para iOS 4+ u OS X 10.6+, Apple agregó
passingTest
series de API enNSMutableArray
, como– indexesOfObjectsPassingTest:
. Una solución con dicha API sería:fuente
Hoy en día puede usar la enumeración inversa basada en bloques. Un código de ejemplo simple:
Resultado:
Otra opción con solo una línea de código:
fuente
De una manera más declarativa, dependiendo de los criterios que coincidan con los elementos a eliminar, podría usar:
@Nathan debería ser muy eficiente
fuente
Aquí está la manera fácil y limpia. Me gusta duplicar mi matriz directamente en la llamada de enumeración rápida:
De esta manera, enumera a través de una copia de la matriz que se está eliminando, ambos con los mismos objetos. Un NSArray contiene punteros de objetos solo, por lo que es una memoria / rendimiento totalmente buena.
fuente
for (LineItem *item in self.lineItems.copy)
Agregue los objetos que desea eliminar a una segunda matriz y, después del ciclo, use -removeObjectsInArray :.
fuente
esto debería hacerlo:
espero que esto ayude...
fuente
¿Por qué no agrega los objetos que se eliminarán a otro NSMutableArray? Cuando termine de iterar, puede eliminar los objetos que ha recopilado.
fuente
¿Qué tal intercambiar los elementos que desea eliminar con el 'n' elemento, 'n-1' elemento y así sucesivamente?
Cuando haya terminado, cambia el tamaño de la matriz a 'tamaño anterior: número de intercambios'
fuente
Si todos los objetos en su matriz son únicos o si desea eliminar todas las apariciones de un objeto cuando se encuentra, puede enumerar rápidamente en una copia de matriz y usar [NSMutableArray removeObject:] para eliminar el objeto del original.
fuente
+arrayWithArray
se ejecuta?La respuesta anterior de benzado es lo que debe hacer para el preformado. En una de mis aplicaciones, removeObjectsInArray tomó un tiempo de ejecución de 1 minuto, solo agregar a una nueva matriz tomó 0.023 segundos.
fuente
Defino una categoría que me permite filtrar usando un bloque, como este:
que luego se puede usar así:
fuente
Una implementación más agradable podría ser usar el método de categoría a continuación en NSMutableArray.
El bloque de predicados se puede implementar para procesar en cada objeto de la matriz. Si el predicado devuelve verdadero, el objeto se elimina.
Un ejemplo de una matriz de fechas para eliminar todas las fechas anteriores:
fuente
Iterar hacia atrás fue mi favorito durante años, pero durante mucho tiempo nunca me encontré con el caso en el que primero se eliminó el objeto 'más profundo' (conteo más alto). Momentáneamente antes de que el puntero pase al siguiente índice, no hay nada y se bloquea.
El camino de Benzado es el más cercano a lo que hago ahora, pero nunca me di cuenta de que habría una reorganización de la pila después de cada eliminación.
bajo Xcode 6 esto funciona
fuente