for each loop in Objective-C for accessing NSMutable dictionary
Asked Answered
S

7

285

I am finding some difficulty in accessing mutable dictionary keys and values in Objective-C.

Suppose I have this:

NSMutableDictionary *xyz=[[NSMutableDictionary alloc] init];

I can set keys and values. Now, I just want to access each key and value, but I don't know the number of keys set.

In PHP it is very easy, something as follows:

foreach ($xyz as $key => $value)

How is it possible in Objective-C?

Sandpiper answered 26/1, 2010 at 23:3 Comment(0)
B
689
for (NSString* key in xyz) {
    id value = xyz[key];
    // do stuff
}

This works for every class that conforms to the NSFastEnumeration protocol (available on 10.5+ and iOS), though NSDictionary is one of the few collections which lets you enumerate keys instead of values. I suggest you read about fast enumeration in the Collections Programming Topic.

Oh, I should add however that you should NEVER modify a collection while enumerating through it.

Behah answered 26/1, 2010 at 23:6 Comment(6)
I was wondering, should'nt it have been: for (NSString* key in [xyz allKeys]) ?? Or does it not really matter.Spendthrift
@user76859403 Enumerating a dictionary, in fact, enumerates the keys. for (NSString* key in [xyz allKeys]) is conceptually the same thing.Behah
Use [xyz allKeys] if you need to mutate the dictionary while enumerating the keys. See my answer below.Rau
Is this loop return same order all the time? Or do we need another way to use it for TableView sections?Gust
@ymutlu, Cocoa dictionaries are unordered. This means that the order in which they iterate may change whenever you modify them.Behah
But doesn't this result in another O(log n) lookup with the subscripting operator for each key -- which would ideally not be needed when iterating over a dictionary? Or is there some magic going on behind the scenes that really makes this fast?Desmund
W
102

Just to not leave out the 10.6+ option for enumerating keys and values using blocks...

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

If you want the actions to happen concurrently:

[dict enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent
                              usingBlock:^(id key, id object, BOOL *stop) {
    NSLog(@"%@ = %@", key, object);
}];
Waterscape answered 27/1, 2010 at 5:43 Comment(7)
3 different ways to enumerate through a dictionary . . . heh, this reminds me of the "Strangest Language Feature" answer where VB has 7 different kinds of loops.Launcher
Well... objectEnumerator was added in 1994, the for(... in ...) in about 2006, and Blocks were added in 2009. The block form of enumeration is natural fallout.Earplug
This seems to be faster than the standard for...in construct: oneofthesedaysblog.com/block-vs-for-in-objective-c-enumerationMckeon
@lkraider, I commented on that post, which actually compares block-based enumeration with an old for loop, not the for...in construct.Waterscape
What is the *stop for? How do you make use of it? Any reference?Catinacation
Set *stop = YES;to stop any further enumeration inside the blockSynoptic
Also, due to the nature of Objective C and its missing ability for method overloading, you can give types for the key and object. For example: ... enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, UIImage *img, BOOL *stop) { ....Maccabees
R
16

If you need to mutate the dictionary while enumerating:

for (NSString* key in xyz.allKeys) {
    [xyz setValue:[NSNumber numberWithBool:YES] forKey:key];
}
Rau answered 3/6, 2013 at 20:10 Comment(1)
The reason is allKeys returns a copy of keys. So it's safe to mutate the dictionary.Damask
P
5

I suggest you to read the Enumeration: Traversing a Collection’s Elements part of the Collections Programming Guide for Cocoa. There is a sample code for your need.

Pirali answered 26/1, 2010 at 23:5 Comment(1)
Those are good links, although the code @Behah posted is much simpler and faster, if you can build for 10.5+ or iPhone.Waterscape
C
5

The easiest way to enumerate a dictionary is

for (NSString *key in tDictionary.keyEnumerator) 
{
    //do something here;
}

where tDictionary is the NSDictionary or NSMutableDictionary you want to iterate.

Clementinaclementine answered 17/12, 2013 at 10:17 Comment(0)
L
3

Fast enumeration was added in 10.5 and in the iPhone OS, and it's significantly faster, not just syntactic sugar. If you have to target the older runtime (i.e. 10.4 and backwards), you'll have to use the old method of enumerating:

NSDictionary *myDict = ... some keys and values ...
NSEnumerator *keyEnum = [myDict keyEnumerator];
id key;

while ((key = [keyEnum nextObject]))
{
    id value = [myDict objectForKey:key];
    ... do work with "value" ...
}

You don't release the enumerator object, and you can't reset it. If you want to start over, you have to ask for a new enumerator object from the dictionary.

Launcher answered 27/1, 2010 at 0:40 Comment(0)
G
2

You can use -[NSDictionary allKeys] to access all the keys and loop through it.

Gonnella answered 26/1, 2010 at 23:7 Comment(1)
True, but this does create an extra autoreleased array, which can be quite wasteful, especially for dictionaries with lots of keys.Waterscape

© 2022 - 2024 — McMap. All rights reserved.