简体   繁体   中英

Sort NSArray of NSDictionary objects using NSSortDescriptor

I have an array of dictionaries that contain information about high scores. I am trying to figure out how to sort them by the different values in the dictionaries but cannot get it to work.

An example shown below attempts to sort by "Score":

NSDictionary *highScoreDictionary1 = @{@"Score" : @52, @"Duration" : @230 , @"Date" : [NSDate date]};
NSDictionary *highScoreDictionary2 =  @{@"Score" : @23, @"Duration" : @230 , @"Date" : [NSDate date]};
NSDictionary *highScoreDictionary3 = @{@"Score" : @35, @"Duration" : @230 , @"Date" : [NSDate date]};

NSArray *highScoresArray = @[highScoreDictionary1, highScoreDictionary2, highScoreDictionary3];

NSSortDescriptor *highScoreSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"Score" ascending:YES];  // Sort by Score
NSArray *sortDescriptorArray = [NSArray arrayWithObject:highScoreSortDescriptor];

[highScoresArray sortedArrayUsingDescriptors:sortDescriptorArray];

The output I get from NSLog(@"sorted array of dictionaries: %@", highScoresArray); is:

sorted array of dictionaries: (
        {
        Date = "2014-09-01 19:38:00 +0000";
        Duration = 230;
        Score = 52;
    },
        {
        Date = "2014-09-01 19:38:00 +0000";
        Duration = 230;
        Score = 23;
    },
        {
        Date = "2014-09-01 19:38:00 +0000";
        Duration = 230;
        Score = 35;
    }
)

How do I remedy this? Am I missing something here because it seems that the dictionaries are not being sorted by score.

highScoresArray = [highScoresArray sortedArrayUsingDescriptors:sortDescriptorArray];

You're trying to sort an NSArray, which is immutable. You need to use the sort function to create a mutable array, ie

replace your:

[highScoresArray sortedArrayUsingDescriptors:sortDescriptorArray];

with:

NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:[highScoresArray sortedArrayUsingDescriptors:@[highScoreSortDescriptor]]];

I have tested this and it seems to work.

Try this approach

NSArray *sortedArray;
sortedArray = [highScoresArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
    NSDictionary *first = (NSDictionary*)a;
    NSDictionary *second = (NSDictionary*)b;

    int firstScore = [first objectForKey:@"score"];
    int secondScore = [second objectForKey:@"score"];

    if(firstScore > secondScore)
    {
        return NSOrderedDescending;
    }
    else if (firstScore < secondScore)
    {
        return NSOrderedAscending;
    }

    return NSOrderedSame;
}];

Got the code from here

If you would like, here's my own sorting method which I implemented manually, in your case just use it like this

// you could also pass "DESC" for descending order
NSMutableArray* copiedArray = [[NSMutableArray alloc] initWithArray:highScoresArray];
[self sortArray:copiedArray inOrder:@"ASC" basedOnField:@"Score" args:-1];
// Now copiedArray contains a sorted array :)

Here's the full code (2 methods, one main and one helper), copy these to some class so the above code would work.

/*
 * This method sorts a given array based on the given field name and the given sorting order
 * fieldName could be nil if the comparison shall happen directly on the array items
 * args contain the array index of the value to compare if "field name" pointed to an array or -1
 */
- (void)sortArray:(NSMutableArray*)array
          inOrder:(NSString*)sortingOrder
     basedOnField:(NSString*)fieldName
             args:(int)args {
    for (int i = 1; i < array.count; i++) {
        // Start the insertion sort algorithm
        int j = i;

        // Get the current value and one before it
        id currentValue = [self itemInArray:array
                                    atIndex:j
                                  fieldName:fieldName
                                       args:args];

        id previousValue = [self itemInArray:array
                                     atIndex:j-1
                                   fieldName:fieldName
                                        args:args];

        // Set the comparison result based on the user request
        NSComparisonResult requiredResult = NSOrderedDescending;
        if ([sortingOrder compare:@"ASC"] == NSOrderedSame) {
            requiredResult = NSOrderedAscending;
        }

        while ((j > 0) && ([previousValue compare:currentValue] == requiredResult)) {
            // Swap the current and previous objects
            id temp = array[j];
            array[j] = array[j-1];
            array[j-1] = temp;

            // Get back one step and get the new current and previous values
            j--;
            if (j == 0) {
                break;
            }

            currentValue = [self itemInArray:array
                                     atIndex:j
                                   fieldName:fieldName
                                        args:args];

            previousValue = [self itemInArray:array
                                      atIndex:j-1
                                    fieldName:fieldName
                                         args:args];
        }
    }
}

// This method gets an item from the array based on the given index and the field name if the item is an object, as well as a specific member of that item if it's an array (index is passed in args)
- (id)itemInArray:(NSArray*)array
          atIndex:(int)index
        fieldName:(NSString*)fieldName
             args:(int)args {

    // Get the item at the given index
    id value = array[index];

    // Get the sepcific field from it if it's an object
    if (fieldName != nil) {
        value = [value valueForKey:fieldName];
    }

    // Get the specific value if the field is an array
    if ([value isKindOfClass:[NSArray class]]) {
        value = value[args];
    }

    return value;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM