Existe-t-il un moyen d'itérer sur un dictionnaire?

200

Je sais NSDictionariesque quelque chose où vous avez besoin d'un keypour obtenir un value. Mais comment puis-je répéter sur tout keyset valuesdans un NSDictionary, afin que je sache quelles sont les clés et quelles sont les valeurs? Je sais qu'il y a quelque chose appelé for-in-loop in JavaScript. Y a-t-il quelque chose de similaire dans Objective-C?

Alex Cio
la source
Merci pour ce post. En cas d'itération dans la Swiftsyntaxe, reportez-vous à cet article: stackoverflow.com/a/24111700/419348
AechoLiu

Réponses:

323

Oui, NSDictionaryprend en charge l'énumération rapide. Avec Objective-C 2.0, vous pouvez le faire:

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

La méthode alternative (que vous devez utiliser si vous ciblez Mac OS X avant 10.5, mais que vous pouvez toujours utiliser sur 10.5 et iPhone) consiste à utiliser 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
la source
2
Syntaxe moderne ObjC: NSLog (@ "key =% @ value =% @", key, myDict [key]);
geowar
@Darthenius en raison des récentes optimisations, l'énumération rapide est à nouveau plus rapide que celle basée sur des blocs, du moins dans certains cas. Mais si le problème que vous résolvez vous permet d'utiliser l'option simultanée, l'approche basée sur les blocs peut être plus rapide.
Zev Eisenberg
@ZevEisenberg Voir la fin de mon message.
Rok Strniša
Oups, j'ai cliqué sur votre lien, ci-dessus, pour l'ouvrir dans un nouvel onglet, et je n'ai même pas remarqué qui l'a écrit ou qu'il était sur cette même page. Si vous pouvez toujours modifier le commentaire ci-dessus, vous voudrez peut-être, afin que les lecteurs paresseux ne se trompent pas.
Zev Eisenberg
153

L'approche par bloc évite d'exécuter l'algorithme de recherche pour chaque clé :

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

Même si elle NSDictionaryest implémentée sous forme de table de hachage (ce qui signifie que le coût de recherche d'un élément est O(1)), les recherches ralentissent toujours votre itération d' un facteur constant .

Mes mesures montrent que pour un dictionnaire dde nombres ...

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

... résumer les chiffres avec l'approche par blocs ...

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

... plutôt que l'approche en boucle ...

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

... est environ 40% plus rapide .

EDIT : Le nouveau SDK (6.1+) semble optimiser l'itération de la boucle, donc l'approche en boucle est maintenant environ 20% plus rapide que l'approche en bloc , au moins pour le cas simple ci-dessus.

Rok Strniša
la source
Qu'en est-il dans iOS 10/11, lequel est le plus rapide?
Supertecnoboff
élégant, j'adore!
YvesLeBorg
10

Il s'agit d'une itération utilisant une approche par blocs:

    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.
    }];

Avec l'auto-complétion est très rapide à définir, et vous n'avez pas à vous soucier de l'écriture de l'enveloppe d'itération.

Javier Calatrava Llavería
la source
Merci .. Bonne solution si vous avez besoin de muter NSMutableDictionarydans le processus
jose920405