{"id":697,"date":"2009-10-25T18:02:19","date_gmt":"2009-10-25T17:02:19","guid":{"rendered":"http:\/\/t-machine.org\/?p=697"},"modified":"2009-10-25T18:02:19","modified_gmt":"2009-10-25T17:02:19","slug":"how-to-sort-an-nsdictionary-on-iphone","status":"publish","type":"post","link":"http:\/\/new.t-machine.org\/index.php\/2009\/10\/25\/how-to-sort-an-nsdictionary-on-iphone\/","title":{"rendered":"How to sort an NSDictionary on iPhone"},"content":{"rendered":"<p>(because I googled it, and on the first page of hits I couldn&#8217;t find any copy\/pasteable source for this common problem, here&#8217;s an answer with (poor) public domain source code)<br \/>\n<!--more--><br \/>\nIn Objective-C \/ Cocoa, you cannot sort a dictionary (which is what I&#8217;m about to fix&#8230;). You cannot place any kind of persistent ordering onto a dictionary.<\/p>\n<p>(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&#8217;s LinkedHashMap&#8230;)<\/p>\n<p>I would prefer a standard-lib solution, especially since it&#8217;ll have all the features, and probably no bugs &#8211; and it&#8217;s more likely to be optimized for performance. But, since Apple hasn&#8217;t given us one, here&#8217;s the lazy-and-quick way to do it:<\/p>\n<ol>\n<li>Make a class that includes an Array and a Dictionary\n<li>copy\/paste the method signatures from NSDictionary and NSMutableDictionary into your new class\n<li>implement each method with a call to your embedded dictionary\n<li>EXCEPT for the methods:\n<ul>\n<li>allKeys\n<li>setObject:forKey:\n<li>setValue:forKey:\n<li>removeAllObjects\n<li>removeObjectForKey:\n<li> &#8230;[and all the methods to do with enumeration and init*  &#8211; but I don&#8217;t have time to do all those, too much hassle]&#8230;\n<\/ul>\n<li>&#8230;for those methods, update the embedded Array as well, and for &#8220;allKeys&#8221; specifically: return a copy of the Array, not the keys from your dictionary\n<\/ol>\n<p>It&#8217;s that easy &#8230; and yet, because Apple didn&#8217;t do it, you have to re-write this class every time you start a new project. Sigh.<\/p>\n<p>There&#8217;s one issue with this &#8211; for the allKeys method, I did a &#8220;copy&#8221;, which I probably shouldn&#8217;t have (I wasn&#8217;t really thinking carefully about i), which means you&#8217;ll have to remember to release the reference you receive &#8211; or else change that to:<\/p>\n<blockquote><p>\nreturn underlyingArray;\n<\/p><\/blockquote>\n<p>or:<\/p>\n<blockquote><p>\nreturn [[underlyingArray copy] autorelease];\n<\/p><\/blockquote>\n<p>or, perhaps best of all:<\/p>\n<blockquote><p>\nreturn [NSArray arrayWithContentsOfArray: underlyingArray]; \/\/ returns an Immutable result; nice and safe!\n<\/p><\/blockquote>\n<p>Here&#8217;s my quick hack code, in its entirety:<\/p>\n<blockquote><p>\n<code><br \/>\n#import \"SortedDictionary.h\"<\/p>\n<p>@implementation SortedDictionary<br \/>\n@synthesize underlyingArray, underlyingDictionary;<\/p>\n<p>-(id) init<br \/>\n{<br \/>\n\tif( self = [super init] )<br \/>\n\t{<br \/>\n\t\tunderlyingArray = [[NSMutableArray alloc] init];<br \/>\n\t\tunderlyingDictionary = [[NSMutableDictionary alloc] init];<br \/>\n\t}<br \/>\n\treturn self;<br \/>\n}<\/p>\n<p>-(id)objectForKey:(id)aKey<br \/>\n{<br \/>\n\treturn [underlyingDictionary objectForKey:aKey];<br \/>\n}<\/p>\n<p>-(id)valueForKey:(NSString *)key<br \/>\n{<br \/>\n\treturn [underlyingDictionary valueForKey:key];<br \/>\n}<\/p>\n<p>-(NSUInteger)count<br \/>\n{<br \/>\n\treturn [underlyingDictionary count];<br \/>\n}<\/p>\n<p>\/** Ordered... *\/<br \/>\n-(NSArray *)allKeys<br \/>\n{<br \/>\n\treturn [underlyingArray copy];<br \/>\n}<\/p>\n<p>-(NSArray *)allValues<br \/>\n{<br \/>\n\treturn [underlyingDictionary allValues];<br \/>\n}<\/p>\n<p>\/** Preserves current order, or does ordered ADD if the key isn't already present *\/<br \/>\n-(void)setObject:(id)anObject forKey:(id)aKey<br \/>\n{<br \/>\n\tif( [underlyingDictionary objectForKey:aKey] == nil )<br \/>\n\t{<br \/>\n\t\t[self addObject:anObject forKey:aKey];<br \/>\n\t}<br \/>\n\telse<br \/>\n\t{<br \/>\n\t\t[underlyingDictionary setObject:anObject forKey:aKey];<br \/>\n\t}<br \/>\n}<\/p>\n<p>\/** Preserves current order, or does ordered ADD if the key isn't already present *\/<br \/>\n-(void)setValue:(id)value forKey:(NSString *)key;<br \/>\n{<br \/>\n\tif( [underlyingDictionary objectForKey:key] == nil )<br \/>\n\t{<br \/>\n\t\t[self addValue:value forKey:key];<br \/>\n\t}<br \/>\n\telse<br \/>\n\t{<br \/>\n\t\t[underlyingDictionary setValue:value forKey:key];<br \/>\n\t}<br \/>\n}<\/p>\n<p>-(void)removeObjectForKey:(id)aKey<br \/>\n{<br \/>\n\t[underlyingArray removeObject:aKey];<br \/>\n\t[underlyingDictionary removeObjectForKey:aKey];<br \/>\n}<\/p>\n<p>-(void)removeAllObjects<br \/>\n{<br \/>\n\t[underlyingArray removeAllObjects];<br \/>\n\t[underlyingDictionary removeAllObjects];<br \/>\n}<\/p>\n<p>-(void)addObject:(id)anObject forKey:(id)aKey<br \/>\n{<br \/>\n\tif( [underlyingDictionary objectForKey:aKey] == nil )<br \/>\n\t{<br \/>\n\t\t[underlyingArray addObject:aKey];<br \/>\n\t\t[underlyingDictionary setObject:anObject forKey:aKey];<br \/>\n\t}<br \/>\n\telse<br \/>\n\t{<br \/>\n\t\tNSLog( @\"[%@] ERROR: attempted to addObject a key that already exists in this dictionary: %@\", [self class], aKey );<br \/>\n\t}<br \/>\n}<\/p>\n<p>-(void)addValue:(id)value forKey:(NSString *)key<br \/>\n{<br \/>\n\tif( [underlyingDictionary objectForKey:key] == nil )<br \/>\n\t{<br \/>\n\t\t[underlyingArray addObject:key];<br \/>\n\t\t[underlyingDictionary setValue:value forKey:key];<br \/>\n\t}<br \/>\n\telse<br \/>\n\t{<br \/>\n\t\tNSLog( @\"[%@] ERROR: attempted to addValue a key that already exists in this dictionary: %@\", [self class], key );<br \/>\n\t}<br \/>\n}<\/p>\n<p>@end<br \/>\n<\/code>\n<\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>(because I googled it, and on the first page of hits I couldn&#8217;t find any copy\/pasteable source for this common problem, here&#8217;s an answer with (poor) public domain source code)<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[51,20],"tags":[],"_links":{"self":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/697"}],"collection":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/comments?post=697"}],"version-history":[{"count":0,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/697\/revisions"}],"wp:attachment":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/media?parent=697"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/categories?post=697"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/tags?post=697"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}