219

I know NSDictionaries as something where you need a key in order to get a value. But how can I iterate over all keys and values in a NSDictionary, so that I know what keys there are, and what values there are? I know there is something called a for-in-loop in JavaScript. Is there something similar in Objective-C?

Alex Cio
  • 6,014
  • 5
  • 44
  • 74
  • Thank for this post. If iterating in `Swift` syntax, refer this post: https://stackoverflow.com/a/24111700/419348 – AechoLiu Jul 20 '18 at 05:13
  • You may explore my humble research of dictionary iterating technics: https://stackoverflow.com/questions/71626497/obj-c-extracting-all-values-from-nsdictionary-without-copying – BorisV Apr 06 '22 at 09:08

3 Answers3

358

Yes, NSDictionary supports fast enumeration. With Objective-C 2.0, you can do this:

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

The alternate method (which you have to use if you're targeting Mac OS X pre-10.5, but you can still use on 10.5 and iPhone) is to use an 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]);
Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 2
    ObjC modern syntax: NSLog(@"key=%@ value=%@", key, myDict[key]); – geowar Feb 08 '13 at 17:14
  • @Darthenius due to recent optimizations, fast enumeration is again faster than block-based, at least in certain cases. But if the problem you are solving allows you to use the concurrent option, the block-based approach may be faster. – Zev Eisenberg May 12 '14 at 20:41
  • @ZevEisenberg See the end of my post. – Rok Strniša May 12 '14 at 20:45
  • Oops, I clicked your link, above, to open in a new tab, and didn’t even notice who wrote it or that it was on this same page. If you can still edit the above comment, you might want to, so that lazy readers don’t get the wrong idea. – Zev Eisenberg May 12 '14 at 20:48
162

The block approach avoids running the lookup algorithm for every key:

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

Even though NSDictionary is implemented as a hashtable (which means that the cost of looking up an element is O(1)), lookups still slow down your iteration by a constant factor.

My measurements show that for a dictionary d of numbers ...

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

... summing up the numbers with the block approach ...

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

... rather than the loop approach ...

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

... is about 40% faster.

EDIT: The new SDK (6.1+) appears to optimise loop iteration, so the loop approach is now about 20% faster than the block approach, at least for the simple case above.

Rok Strniša
  • 6,781
  • 6
  • 41
  • 53
10

This is iteration using block approach:

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

With autocompletion is very fast to set, and you do not have to worry about writing iteration envelope.

Bhavin Bhadani
  • 22,224
  • 10
  • 78
  • 108