279

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?

Cœur
  • 32,421
  • 21
  • 173
  • 232
Sagar R. Kothari
  • 23,587
  • 48
  • 158
  • 226

7 Answers7

677
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.

Iulian Onofrei
  • 7,489
  • 8
  • 59
  • 96
zneak
  • 124,558
  • 39
  • 238
  • 307
  • 4
    I was wondering, should'nt it have been: for (NSString* key in [xyz allKeys]) ?? Or does it not really matter. – Just a coder Apr 02 '12 at 04:23
  • 8
    @user76859403 Enumerating a dictionary, in fact, enumerates the keys. `for (NSString* key in [xyz allKeys])` is conceptually the same thing. – zneak Apr 02 '12 at 05:09
  • Use [xyz allKeys] if you need to mutate the dictionary while enumerating the keys. See my answer below. – Brody Robertson Jun 03 '13 at 20:05
  • Is this loop return same order all the time? Or do we need another way to use it for TableView sections? – ymutlu Oct 31 '14 at 14:55
  • 1
    @ymutlu, Cocoa dictionaries are unordered. This means that the order in which they iterate may change whenever you modify them. – zneak Oct 31 '14 at 17:43
101

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);
}];
Quinn Taylor
  • 43,807
  • 16
  • 109
  • 128
  • 2
    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. – dreamlax Jan 27 '10 at 05:47
  • 3
    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. – bbum Feb 08 '10 at 04:25
  • This seems to be faster than the standard *for...in* construct: http://oneofthesedaysblog.com/block-vs-for-in-objective-c-enumeration – lkraider Jul 08 '11 at 20:40
  • 1
    @lkraider, I commented on that post, which actually compares block-based enumeration with an old for loop, not the for...in construct. – Quinn Taylor Aug 09 '11 at 00:27
  • What is the `*stop` for? How do you make use of it? Any reference? – borisdiakur Sep 04 '12 at 20:27
  • Set *stop = YES;to stop any further enumeration inside the block – Parag Bafna Oct 12 '12 at 09:05
  • 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) { ...`. – mkko Jan 26 '14 at 17:14
16

If you need to mutate the dictionary while enumerating:

for (NSString* key in xyz.allKeys) {
    [xyz setValue:[NSNumber numberWithBool:YES] forKey:key];
}
Brody Robertson
  • 8,107
  • 2
  • 40
  • 42
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.

P.J.Radadiya
  • 1,372
  • 10
  • 16
Avinash
  • 4,016
  • 1
  • 21
  • 16
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.

rstackhouse
  • 1,871
  • 19
  • 24
Laurent Etiemble
  • 25,779
  • 5
  • 52
  • 81
  • Those are good links, although the code @zneak posted is much simpler and faster, if you can build for 10.5+ or iPhone. – Quinn Taylor Jan 26 '10 at 23:41
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.

Quinn Taylor
  • 43,807
  • 16
  • 109
  • 128
dreamlax
  • 89,489
  • 28
  • 156
  • 207
2

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

Quinn Taylor
  • 43,807
  • 16
  • 109
  • 128
gcamp
  • 14,491
  • 4
  • 52
  • 81
  • 2
    True, but this does create an extra autoreleased array, which can be quite wasteful, especially for dictionaries with lots of keys. – Quinn Taylor Jan 27 '10 at 06:11