12

estoy usando la enumeración basada en bloques similar al siguiente código:Cómo eliminar elementos en NSMutableArray o NSMutableDictionary durante la enumeración?

[[[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType] 
    enumerateObjectsWithOptions:NSEnumerationConcurrent 
        usingBlock:^(id coaItem, NSUInteger idx, BOOL *stop) { 
// block code here 
}] 

quisiera eliminar algunos de los objetos durante el proceso de enumeración en función de los valores de sus objetos.

¿Cómo podría hacer esto? Sé que manipular una matriz mutable o un diccionario (NSMutableArray o NSMutableDictionary) durante la enumeración generalmente no es posible.

¿Cuál sería la mejor manera de implementar esto?

Gracias!

Respuesta

38

Dado que no puede eliminar objetos de una matriz o diccionario durante la enumeración, deberá acumular los elementos que desee eliminar y luego eliminarlos después de la enumeración.

Si usted está tratando con una matriz, que sólo puede acumular los índices .:

NSMutableIndexSet *indexesToDelete = [NSMutableIndexSet indexSet]; 
NSUInteger currentIndex = 0; 

for (id obj in yourArray) { 
    //do stuff with obj 
    if (shouldBeDeleted(obj)) { 
     [indexesToDelete addIndex:currentIndex]; 
    } 
    currentIndex++; 
} 

[yourArray removeObjectsAtIndexes:indexesToDelete]; 

Dado que el orden de las claves en un NSDictionary no está definida, para un NSMutableDictionary tendrá que acumular teclas en su lugar:

NSMutableArray *keysToDelete = [NSMutableArray array]; 

for (id obj in [yourDictionary keyEnumerator]) { 
    //do stuff with obj 
    if (shouldBeDeleted(obj)) { 
     [keysToDelete addObject:obj]; 
    } 
} 

[yourDictionary removeObjectsForKeys:keysToDelete]; 

es lo mismo que si usted está enumerando con un bloque. Declare el enumerador en el mismo alcance donde declara el bloque y se conservará y solo funcionará.

También vale la pena mirar esta pregunta desde hace 3 años: Best way to remove from NSMutableArray while iterating?.

+0

Gracias, Yuji! Me ayudaste mucho. – AlexR

1

Otra opción, con una matriz, es utilizar un bucle convencional for, con la matriz de count como el límite. Entonces uno debe saber si un elemento se elimina de un lugar <= el índice (en cuyo caso el índice debe reducirse) o > que el índice (en cuyo caso el índice no se modifica más que el incremento de la declaración for).

Para un diccionario, primero puede crear una matriz con allKeys y luego iterar a través de la matriz. En este caso, no es necesario manipular los valores del índice.

7

Si construye un conjunto de índices durante la enumeración o modifica la matriz durante la enumeración, tendrá que renunciar a NSEnumerationConcurrent, porque la mayoría de los objetos de Cocoa no se pueden modificar simultáneamente de manera segura desde múltiples hilos.

De todos modos, el enfoque más simple (pero tal vez no el más eficiente) es simplemente enumerar una copia del contenedor.

Para una matriz, puede enumerar una copia en reversa. Supongo que a medida que se enumeran cada elemento, puede decidir eliminar ese elemento, pero no otros elementos enumerados anteriormente o que aún no se han enumerado.

NSMutableArray *array = [[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType]; 
[[array copy] enumerateObjectsWithOptions: NSEnumerationReverse 
    usingBlock:^(id coaItem, NSUInteger idx, BOOL *stop) { 
    if ([self objectIsTooUglyToExist:coaItem]) 
     [array removeObjectAtIndex:idx]; 
}] 

Tiene que enumerar la matriz al revés para evitar cambiar la parte no enumerada aún de la matriz.

Para un diccionario, sólo puede enumerar una copia sin opciones especiales:

NSMutableDictionary *dictionary = someDictionary; 
[[dictionary copy] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 
    if ([self object:obj isTooUglyToExistAtKey:key]) 
     [dictionary removeObjectForKey:key]; 
}]; 
+0

Gracias por la sugerencia sobre la opción "NSEnumerationConcurrent", Rob! – AlexR

Cuestiones relacionadas