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, i.e.
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;
}