Sorting with sort descriptors
Sorting an array of NSNumber
elements in Objective-C is fairly straight forward:
NSArray *unsortedArray = @[@(9), @(5), @(1), @(2), @(4)];
NSArray *sortedArray = [unsortedArray sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"sortedArray: %@", sortedArray);
But what happens if we have something more complex, where the value being compared isn't the type itself but a property of that type?
The simple approach above won't work. Instead, we need to use a NSSortDescriptor
. NSSortDescriptor
allows us to sort the elements in an array based on that element's properties rather than the element itself.
Take the following class:
@interface WBWReview : NSObject
@property (nonatomic, assign) NSUInteger ratings;
@property (nonatomic, assign) NSUInteger votes;
@end
@implementation WBWReview
#pragma mark - Init
- (instancetype)initWithRatings:(NSUInteger)ratings votes:(NSUInteger)votes {
self = [super init];
if (self) {
_ratings = ratings;
_votes = votes;
}
return self;
}
@end
If we wanted to sort an array of Review
instances by ratings
then we would configure a NSSortDescriptor
instance like:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"ratings" ascending:NO];
NSArray *sortedArray = [unsortedArray sortedArrayUsingDescriptors:@[sortDescriptor]];
NSLog(@"sortedArray: %@", sortedArray);
With the above code snippet, we configure an NSSortDescriptor
instance to sort the unsortedArray
using the ratings
property of each element in that array, in descending order.
If the elements of the array don't have a
ratings
property, then the above code would crash.
And if we wanted to sort by votes
:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"votes" ascending:NO];
NSArray *sortedArray = [unsortedArray sortedArrayUsingDescriptors:@[sortDescriptor]];
NSLog(@"sortedArray: %@", sortedArray);
Almost the same, the only thing that changed between the two examples is that the key
of the NSSortDescriptor
instance: from ratings
to votes
.
While sorting by one property is super, it's possible to combine NSSortDescriptor
instances and sort by more than one property.
If instead we wanted to sort by votes
and then ratings
then we would flip the order of the NSSortDescriptor
instances when applied to the array:
NSSortDescriptor *ratingsSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"ratings" ascending:NO];
NSSortDescriptor *votesSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"votes" ascending:NO];
NSArray *sortedArray = [unsortedArray sortedArrayUsingDescriptors:@[ratingsSortDescriptor, votesSortDescriptor]];
NSLog(@"sortedArray: %@", sortedArray);
The above code snippet would sort unsortedArray
first by ratings
and then if two (or more) Review
instances had the same ratings
value by votes
.
Straightforward stuff but super-powerful.
What do you think? Let me know by getting in touch on Twitter - @wibosco