42

I have an NSMutableDictionary with integer values, and I'd like to get an array of the keys, sorted ascending by their respective values. For example, with this dictionary:

mutableDict = {
    "A" = 2,
    "B" = 4,
    "C" = 3,
    "D" = 1,
}

I'd like to end up with the array ["D", "A", "C", "B"]. My real dictionary is much larger than just four items, of course.

jscs
  • 63,694
  • 13
  • 151
  • 195
Eric
  • 4,063
  • 2
  • 27
  • 49
  • There are dozens of elements, I want a new array of Keys sorted by the ascending values of the keys that contain integers – Eric Mar 14 '12 at 19:37
  • Josh, for what I'm trying to do, yes they are all integers. Thanks for everyone's help! – Eric Mar 14 '12 at 19:44

5 Answers5

66

The NSDictionary Method keysSortedByValueUsingComparator: should do the trick.

You just need a method returning an NSComparisonResult that compares the object's values.

Your Dictionary is

NSMutableDictionary * myDict;

And your Array is

NSArray *myArray;

myArray = [myDict keysSortedByValueUsingComparator: ^(id obj1, id obj2) {

     if ([obj1 integerValue] > [obj2 integerValue]) {

          return (NSComparisonResult)NSOrderedDescending;
     }
     if ([obj1 integerValue] < [obj2 integerValue]) {

          return (NSComparisonResult)NSOrderedAscending;
     }

     return (NSComparisonResult)NSOrderedSame;
}];

Just use NSNumber objects instead of numeric constants.

BTW, this is taken from: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Collections/Articles/Dictionaries.html

Cœur
  • 37,241
  • 25
  • 195
  • 267
Hermann Klecker
  • 14,039
  • 5
  • 48
  • 71
  • 4
    Richard's proposal is more elegant than mine because NSNumber already comes with a suitable compare function but mine is probably more general. – Hermann Klecker Mar 14 '12 at 19:41
  • 1
    In most cases, this solution will work fine, but what my answer has is the ability to support other types that respond to the `-compare:` – Richard J. Ross III Mar 14 '12 at 19:47
  • This will returns the key list , is there anyway that can get the dictionary directly , instead of having list of sorted keys in array – Mr.G Sep 27 '16 at 09:41
  • @Mr.G, no there is no way as NSDictionary and NSMutableDictionare are not sorted. You cannot sort them and expect them to be sorted afterwards. – Hermann Klecker Sep 27 '16 at 12:52
27

NSDictionary has this neat method called allKeys.

If you want the array to be sorted though, keysSortedByValueUsingComparator: should do the trick.

Richard's solution also works but makes some extra calls you don't necessarily need:

// Assuming myDictionary was previously populated with NSNumber values.
NSArray *orderedKeys = [myDictionary keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2){
    return [obj1 compare:obj2];
}];
harpun
  • 4,022
  • 1
  • 36
  • 40
  • I edited my question, I thought it was implied the keys have to in order by key value – Eric Mar 14 '12 at 19:30
  • I'm asking for help on the sorting. – Eric Mar 14 '12 at 19:32
  • `keysSortedByValueUsingComparator:` will do this sorting. –  Mar 14 '12 at 19:34
  • could you write out a simple comparator block for ascending integers like I have in the question? I'm new to blocks and block structure. – Eric Mar 14 '12 at 19:36
  • 2
    I'm sure you can at least _try_ to use the method (documentation is linked). If you have problems with it, come back with your coded attempt, I'd be more than happy to help you. –  Mar 14 '12 at 19:40
  • You are missing an 's' in your example code. (I can't edit for you because it is less than 6 letters) – matt Aug 27 '13 at 10:34
16

Here's a solution:

NSDictionary *dictionary; // initialize dictionary
NSArray *sorted = [[dictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return [[dictionary objectForKey:obj1] compare:[dictionary objectForKey:obj2]];
}];
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • working correctly for strings starting with alphabates only, but when there is a string starting with number 10, its comming between first string starting with "A" and third string starting with "A" – ViruMax Mar 10 '14 at 06:57
  • Check the answer above, it's more intuitive and concise, even though this does the same thing. – nemesis Sep 01 '14 at 16:25
14

The simplest solution:

[dictionary keysSortedByValueUsingSelector:@selector(compare:)]

Rudolf Adamkovič
  • 31,030
  • 13
  • 103
  • 118
  • 1
    how to write that compare function – Pradumna Patil Jun 10 '15 at 14:20
  • Please explain the compare method also. – onCompletion Oct 05 '15 at 05:43
  • For existing types (e.g. NSString), that method already exists. For anything you create yourself, the method should return NSOrderedAscending, NSOrderedSame, or NSOrderedDescending, depending on the order. See the documentation for `compare:options:range:` for more info. – dgatwood Oct 07 '16 at 22:12
2

Here i have done something like this:

NSMutableArray * weekDays = [[NSMutableArray alloc] initWithObjects:@"Sunday",@"Monday",@"Tuesday",@"Wednesday",@"Thursday",@"Friday",@"Saturday", nil];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSMutableArray *dictArray = [[NSMutableArray alloc] init];

for(int i = 0; i < [weekDays count]; i++)
{
    dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:i],@"WeekDay",[weekDays objectAtIndex:i],@"Name",nil];
    [dictArray addObject:dict];
}
NSLog(@"Before Sorting : %@",dictArray);

@try
{
    //for using NSSortDescriptor
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"WeekDay" ascending:YES];
    NSArray *descriptor = @[sortDescriptor];
    NSArray *sortedArray = [dictArray sortedArrayUsingDescriptors:descriptor];
    NSLog(@"After Sorting : %@",sortedArray);

    //for using predicate
    //here i want to sort the value against weekday but only for WeekDay<=5
   int count=5;
    NSPredicate *Predicate = [NSPredicate predicateWithFormat:@"WeekDay <=%d",count];
    NSArray *results = [dictArray filteredArrayUsingPredicate:Predicate];

    NSLog(@"After Sorting using predicate : %@",results);
}
@catch (NSException *exception)
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Sorting cant be done because of some error" message:[NSString stringWithFormat:@"%@",exception] delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil];
    [alert setTag:500];
    [alert show];
    [alert release];
}
tapash
  • 37
  • 3