How to sort an NSDictionary on iPhone

(because I googled it, and on the first page of hits I couldn’t find any copy/pasteable source for this common problem, here’s an answer with (poor) public domain source code)

In Objective-C / Cocoa, you cannot sort a dictionary (which is what I’m about to fix…). You cannot place any kind of persistent ordering onto a dictionary.

(this is a common requirement in modern software, such that you expect any standard lib to contain a core class to handle it. For instance, Java’s LinkedHashMap…)

I would prefer a standard-lib solution, especially since it’ll have all the features, and probably no bugs – and it’s more likely to be optimized for performance. But, since Apple hasn’t given us one, here’s the lazy-and-quick way to do it:

  1. Make a class that includes an Array and a Dictionary
  2. copy/paste the method signatures from NSDictionary and NSMutableDictionary into your new class
  3. implement each method with a call to your embedded dictionary
  4. EXCEPT for the methods:
    • allKeys
    • setObject:forKey:
    • setValue:forKey:
    • removeAllObjects
    • removeObjectForKey:
    • …[and all the methods to do with enumeration and init* – but I don’t have time to do all those, too much hassle]…
  5. …for those methods, update the embedded Array as well, and for “allKeys” specifically: return a copy of the Array, not the keys from your dictionary

It’s that easy … and yet, because Apple didn’t do it, you have to re-write this class every time you start a new project. Sigh.

There’s one issue with this – for the allKeys method, I did a “copy”, which I probably shouldn’t have (I wasn’t really thinking carefully about i), which means you’ll have to remember to release the reference you receive – or else change that to:

return underlyingArray;

or:

return [[underlyingArray copy] autorelease];

or, perhaps best of all:

return [NSArray arrayWithContentsOfArray: underlyingArray]; // returns an Immutable result; nice and safe!

Here’s my quick hack code, in its entirety:


#import "SortedDictionary.h"

@implementation SortedDictionary
@synthesize underlyingArray, underlyingDictionary;

-(id) init
{
if( self = [super init] )
{
underlyingArray = [[NSMutableArray alloc] init];
underlyingDictionary = [[NSMutableDictionary alloc] init];
}
return self;
}

-(id)objectForKey:(id)aKey
{
return [underlyingDictionary objectForKey:aKey];
}

-(id)valueForKey:(NSString *)key
{
return [underlyingDictionary valueForKey:key];
}

-(NSUInteger)count
{
return [underlyingDictionary count];
}

/** Ordered... */
-(NSArray *)allKeys
{
return [underlyingArray copy];
}

-(NSArray *)allValues
{
return [underlyingDictionary allValues];
}

/** Preserves current order, or does ordered ADD if the key isn't already present */
-(void)setObject:(id)anObject forKey:(id)aKey
{
if( [underlyingDictionary objectForKey:aKey] == nil )
{
[self addObject:anObject forKey:aKey];
}
else
{
[underlyingDictionary setObject:anObject forKey:aKey];
}
}

/** Preserves current order, or does ordered ADD if the key isn't already present */
-(void)setValue:(id)value forKey:(NSString *)key;
{
if( [underlyingDictionary objectForKey:key] == nil )
{
[self addValue:value forKey:key];
}
else
{
[underlyingDictionary setValue:value forKey:key];
}
}

-(void)removeObjectForKey:(id)aKey
{
[underlyingArray removeObject:aKey];
[underlyingDictionary removeObjectForKey:aKey];
}

-(void)removeAllObjects
{
[underlyingArray removeAllObjects];
[underlyingDictionary removeAllObjects];
}

-(void)addObject:(id)anObject forKey:(id)aKey
{
if( [underlyingDictionary objectForKey:aKey] == nil )
{
[underlyingArray addObject:aKey];
[underlyingDictionary setObject:anObject forKey:aKey];
}
else
{
NSLog( @"[%@] ERROR: attempted to addObject a key that already exists in this dictionary: %@", [self class], aKey );
}
}

-(void)addValue:(id)value forKey:(NSString *)key
{
if( [underlyingDictionary objectForKey:key] == nil )
{
[underlyingArray addObject:key];
[underlyingDictionary setValue:value forKey:key];
}
else
{
NSLog( @"[%@] ERROR: attempted to addValue a key that already exists in this dictionary: %@", [self class], key );
}
}

@end

7 thoughts on “How to sort an NSDictionary on iPhone”

Comments are closed.