¿Hay alguna forma de iterar sobre un diccionario?

200

NSDictionariescomo algo donde necesitas un keypara obtener un value. Pero, ¿cómo puedo iterar sobre todo keysy valuesen a NSDictionary, para saber qué claves hay y qué valores hay? Sé que hay algo llamado for-in-loop in JavaScript. ¿Hay algo similar en Objective-C?

Alex Cio
fuente
Gracias por este post. Si itera en Swiftsintaxis, consulte esta publicación: stackoverflow.com/a/24111700/419348
AechoLiu el

Respuestas:

323

Sí, NSDictionaryadmite enumeración rápida. Con Objective-C 2.0, puede hacer esto:

// To print out all key-value pairs in the NSDictionary myDict
for(id key in myDict)
    NSLog(@"key=%@ value=%@", key, [myDict objectForKey:key]);

El método alternativo (que debe usar si se dirige a Mac OS X anterior a 10.5, pero aún puede usarlo en 10.5 y iPhone) es usar un NSEnumerator:

NSEnumerator *enumerator = [myDict keyEnumerator];
id key;
// extra parens to suppress warning about using = instead of ==
while((key = [enumerator nextObject]))
    NSLog(@"key=%@ value=%@", key, [myDict objectForKey:key]);
Adam Rosenfield
fuente
2
Sintaxis moderna de ObjC: NSLog (@ "key =% @ value =% @", key, myDict [key]);
geowar
@Darthenius debido a optimizaciones recientes, la enumeración rápida es nuevamente más rápida que la basada en bloques, al menos en ciertos casos. Pero si el problema que está resolviendo le permite usar la opción concurrente, el enfoque basado en bloques puede ser más rápido.
Zev Eisenberg
@ZevEisenberg Ver el final de mi publicación.
Rok Strniša
Vaya, hice clic en su enlace, arriba, para abrir en una nueva pestaña, y ni siquiera noté quién lo escribió o que estaba en esta misma página. Si aún puede editar el comentario anterior, es posible que desee hacerlo, para que los lectores vagos no tengan una idea equivocada.
Zev Eisenberg
153

El enfoque de bloque evita ejecutar el algoritmo de búsqueda para cada clave :

[dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) {
  NSLog(@"%@ => %@", key, value);
}];

Aunque NSDictionaryse implementa como una tabla hash (lo que significa que el costo de buscar un elemento es O(1)), las búsquedas aún ralentizan su iteración por un factor constante .

Mis medidas muestran que para un diccionario dde números ...

NSMutableDictionary* dict = [NSMutableDictionary dictionary];
for (int i = 0; i < 5000000; ++i) {
  NSNumber* value = @(i);
  dict[value.stringValue] = value;
}

... resumiendo los números con el enfoque de bloque ...

__block int sum = 0;
[dict enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSNumber* value, BOOL* stop) {
  sum += value.intValue;
}];

... en lugar del enfoque de bucle ...

int sum = 0;
for (NSString* key in dict)
  sum += [dict[key] intValue];

... es aproximadamente un 40% más rápido .

EDITAR : El nuevo SDK (6.1+) parece optimizar la iteración del bucle, por lo que el enfoque de bucle ahora es aproximadamente un 20% más rápido que el enfoque de bloque , al menos para el caso simple anterior.

Rok Strniša
fuente
¿Qué pasa en iOS 10/11, cuál es más rápido?
Supertecnoboff
elegante, me encanta!
YvesLeBorg
10

Esta es una iteración usando el enfoque de bloque:

    NSDictionary *dict = @{@"key1":@1, @"key2":@2, @"key3":@3};

    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        NSLog(@"%@->%@",key,obj);
        // Set stop to YES when you wanted to break the iteration.
    }];

Con el autocompletado es muy rápido de configurar, y no tiene que preocuparse por escribir un sobre de iteración.

Javier Calatrava Llavería
fuente
Gracias. Buena solución si necesita mutar NSMutableDictionaryel proceso
jose920405