Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSDictionary with ordered keys

I have an NSDictionary (stored in a plist) that I'm basically using as an associative array (strings as keys and values). I want to use the array of keys as part of my application, but I'd like them to be in a specific order (not really an order that I can write an algorithm to sort them into). I could always store a separate array of the keys, but that seems kind of kludgey because I'd always have to update the keys of the dictionary as well as the values of the array, and make sure they always correspond. Currently I just use [myDictionary allKeys], but obviously this returns them in an arbitrary, non-guaranteed order. Is there a data structure in Objective-C that I'm missing? Does anyone have any suggestions on how to more elegantly do this?

like image 491
Andy Bourassa Avatar asked Dec 17 '08 21:12

Andy Bourassa


5 Answers

The solution of having an associated NSMutableArray of keys isn't so bad. It avoids subclassing NSDictionary, and if you are careful with writing accessors, it shouldn't be too hard to keep synchronised.

like image 126
Abizern Avatar answered Oct 03 '22 06:10

Abizern


I'm late to the game with an actual answer, but you might be interested to investigate CHOrderedDictionary. It's a subclass of NSMutableDictionary which encapsulates another structure for maintaining key ordering. (It's part of CHDataStructures.framework.) I find it to be more convenient than managing a dictionary and array separately.

Disclosure: This is open-source code which I wrote. Just hoping it may be useful to others facing this problem.

like image 30
Quinn Taylor Avatar answered Oct 03 '22 08:10

Quinn Taylor


There is no such inbuilt method from which you can acquire this. But a simple logic work for you. You can simply add few numeric text in front of each key while you prepare the dictionary. Like

NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
                       @"01.Created",@"cre",
                       @"02.Being Assigned",@"bea",
                       @"03.Rejected",@"rej",
                       @"04.Assigned",@"ass",
                       @"05.Scheduled",@"sch",
                       @"06.En Route",@"inr",
                       @"07.On Job Site",@"ojs",
                       @"08.In Progress",@"inp",
                       @"09.On Hold",@"onh",
                       @"10.Completed",@"com",
                       @"11.Closed",@"clo",
                       @"12.Cancelled", @"can",
                       nil]; 

Now if you can use sortingArrayUsingSelector while getting all keys in the same order as you place.

NSArray *arr =  [[dict allKeys] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];

At the place where you want to display keys in UIView, just chop off the front 3 character.

like image 44
Rajan Twanabashu Avatar answered Oct 03 '22 08:10

Rajan Twanabashu


If you're going to subclass NSDictionary you need to implement these methods as a minimum:

  • NSDictionary
    • -count
    • -objectForKey:
    • -keyEnumerator
  • NSMutableDictionary
    • -removeObjectForKey:
    • -setObject:forKey:
  • NSCopying/NSMutableCopying
    • -copyWithZone:
    • -mutableCopyWithZone:
  • NSCoding
    • -encodeWithCoder:
    • -initWithCoder:
  • NSFastEnumeration (for Leopard)
    • -countByEnumeratingWithState:objects:count:

The easiest way to do what you want is to make a subclass of NSMutableDictionary that contains its' own NSMutableDictionary that it manipulates and an NSMutableArray to store an ordered set of keys.

If you're never going to encode your objects you could conceivable skip implementing -encodeWithCoder: and -initWithCoder:

All of your method implementations in the 10 methods above would then either go directly through your hosted dictionary or your ordered key array.

like image 42
Ashley Clark Avatar answered Oct 03 '22 08:10

Ashley Clark


My little addition: sorting by numeric key (Using shorthand notations for smaller code)

// the resorted result array
NSMutableArray *result = [NSMutableArray new];
// the source dictionary - keys may be Ux timestamps (as integer, wrapped in NSNumber)
NSDictionary *dict =
@{
  @0: @"a",
  @3: @"d",
  @1: @"b",
  @2: @"c"
};

{// do the sorting to result
    NSArray *arr = [[dict allKeys] sortedArrayUsingSelector:@selector(compare:)];

    for (NSNumber *n in arr)
        [result addObject:dict[n]];
}
like image 28
BananaAcid Avatar answered Oct 03 '22 07:10

BananaAcid