There are several ways to sort an NSArray
object; these methods return a new array: sortedArrayUsingDescriptors:
, sortedArrayUsingFunction:context:
, sortedArrayUsingFunction:context:hint:
, and sortedArrayUsingSelector:
—you can use them with an immutable or a mutable array; these methods sort the contents of a mutable array: sortUsingDescriptors:
, sortUsingFunction:context:
, and sortUsingSelector:
.
Important: If you sort a list that is to be shown to the end user, you should always use a localized comparison.
For a general overview of the issues related to sorting, see Collation Introduction.
The following example illustrates the use of sortedArrayUsingSelector:
,sortedArrayUsingFunction:context:
, and sortedArrayUsingFunction:context:hint:
. The most complex of these methods is sortedArrayUsingFunction:context:hint:
. The hinted sort is most efficient when you have a large array (N entries) that you sort once and then change only slightly (P additions and deletions, where P is much smaller than N). You can reuse the work you did in the original sort by conceptually doing a merge sort between the N “old” items and the P “new” items. To obtain an appropriate hint, you use sortedArrayHint
when the original array has been sorted, and keep hold of it until you need it (when you want to re-sort the the array after it has been modified).
NSInteger alphabeticSort(id string1, id string2, void *reverse) |
{ |
if ((NSInteger *)reverse == NO) { |
return [string2 localizedCaseInsensitiveCompare:string1]; |
} |
return [string1 localizedCaseInsensitiveCompare:string2]; |
} |
NSMutableArray *anArray = |
[NSMutableArray arrayWithObjects:@"aa", @"ab", @"ac", @"ad", @"ae", @"af", @"ag", |
@"ah", @"ai", @"aj", @"ak", @"al", @"am", @"an", @"ao", @"ap", @"aq", @"ar", @"as", @"at", |
@"au", @"av", @"aw", @"ax", @"ay", @"az", @"ba", @"bb", @"bc", @"bd", @"bf", @"bg", @"bh", |
@"bi", @"bj", @"bk", @"bl", @"bm", @"bn", @"bo", @"bp", @"bq", @"br", @"bs", @"bt", @"bu", |
@"bv", @"bw", @"bx", @"by", @"bz", @"ca", @"cb", @"cc", @"cd", @"ce", @"cf", @"cg", @"ch", |
@"ci", @"cj", @"ck", @"cl", @"cm", @"cn", @"co", @"cp", @"cq", @"cr", @"cs", @"ct", @"cu", |
@"cv", @"cw", @"cx", @"cy", @"cz", nil]; |
// note: anArray is sorted |
NSData *sortedArrayHint = [anArray sortedArrayHint]; |
[anArray insertObject:@"be" atIndex:5]; |
NSArray *sortedArray; |
// sort using a selector |
sortedArray = |
[anArray sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; |
// sort using a function |
int reverseSort = NO; |
sortedArray = |
[anArray sortedArrayUsingFunction:alphabeticSort context:&reverseSort]; |
// sort with a hint |
sortedArray = |
[anArray sortedArrayUsingFunction:alphabeticSort |
context:&reverseSort |
hint:sortedArrayHint]; |
Sort descriptors (instances of NSSortDescriptor
) provide a convenient and abstract way to describe a sort ordering. Sort descriptors provide several useful features. You can use them in conjunction with Cocoa bindings to sort the contents of, for example, a table view, and you can use them with Core Data to order the results of a fetch request.
If you use the methods sortedArrayUsingDescriptors:
or sortUsingDescriptors:
, sort descriptors provide an easy way to sort a collection of objects using a number of their properties. Consider the following example. Given an array of dictionaries (custom objects would work in the same way):
NSString *LAST = @"lastName"; |
NSString *FIRST = @"firstName"; |
NSMutableArray *array = [NSMutableArray array]; |
NSArray *sortedArray; |
NSDictionary *dict; |
dict = [NSDictionary dictionaryWithObjectsAndKeys: |
@"Jo", FIRST, @"Smith", LAST, nil]; |
[array addObject:dict]; |
dict = [NSDictionary dictionaryWithObjectsAndKeys: |
@"Joe", FIRST, @"Smith", LAST, nil]; |
[array addObject:dict]; |
dict = [NSDictionary dictionaryWithObjectsAndKeys: |
@"Joe", FIRST, @"Smythe", LAST, nil]; |
[array addObject:dict]; |
dict = [NSDictionary dictionaryWithObjectsAndKeys: |
@"Joanne", FIRST, @"Smith", LAST, nil]; |
[array addObject:dict]; |
dict = [NSDictionary dictionaryWithObjectsAndKeys: |
@"Rupert", FIRST, @"Psmith", LAST, nil]; |
[array addObject:dict]; |
you can sort the contents of the array by last name then first name as follows:
// The results are likely to be shown to a user |
// Note the use of the localizedCaseInsensitiveCompare: selector |
NSSortDescriptor *lastDescriptor = |
[[[NSSortDescriptor alloc] initWithKey:LAST |
ascending:YES |
selector:@selector(localizedCaseInsensitiveCompare:)] autorelease]; |
NSSortDescriptor *firstDescriptor = |
[[[NSSortDescriptor alloc] initWithKey:FIRST |
ascending:YES |
selector:@selector(localizedCaseInsensitiveCompare:)] autorelease]; |
NSArray *descriptors = [NSArray arrayWithObjects:lastDescriptor, firstDescriptor, nil]; |
sortedArray = [array sortedArrayUsingDescriptors:descriptors]; |
It is conceptually and programmatically easy to change the sort ordering and to arrange by first name then last name:
NSSortDescriptor *lastDescriptor = |
[[[NSSortDescriptor alloc] initWithKey:LAST |
ascending:NO |
selector:@selector(localizedCaseInsensitiveCompare:)] autorelease]; |
NSSortDescriptor *firstDescriptor = |
[[[NSSortDescriptor alloc] initWithKey:FIRST |
ascending:NO |
selector:@selector(localizedCaseInsensitiveCompare:)] autorelease]; |
NSArray *descriptors = [NSArray arrayWithObjects:firstDescriptor, lastDescriptor, nil]; |
sortedArray = [array sortedArrayUsingDescriptors:descriptors]; |
In particular, it is straightforward to create the sort descriptors from user input.
By contrast, the following code illustrates the first sorting using a function.
NSInteger lastNameFirstNameSort(id person1, id person2, void *reverse) |
{ |
NSString *name1 = [person1 valueForKey:LAST]; |
NSString *name2 = [person2 valueForKey:LAST]; |
NSComparisonResult comparison = [name1 localizedCaseInsensitiveCompare:name2]; |
if (comparison == NSOrderedSame) { |
name1 = [person1 valueForKey:FIRST]; |
name2 = [person2 valueForKey:FIRST]; |
comparison = [name1 localizedCaseInsensitiveCompare:name2]; |
} |
if ((BOOL *)reverse == NO) { |
return 0 - comparison; |
} |
return comparison; |
} |
BOOL reverseSort = YES; |
sortedArray = [array sortedArrayUsingFunction:lastNameFirstNameSort |
context:&reverseSort]; |
This approach is considerably less flexible.
iOS Note: The predicate classes—NSPredicate
, NSCompoundPredicate
, and
NSComparisonPredicate
—are present only in the Mac OS X version of Foundation.
NSArray
and NSMutableArray
provide methods to filter array contents. NSArray
provides filteredArrayUsingPredicate:
which returns a new array containing objects in the receiver that match the specified predicate. NSMutableArray
adds filterUsingPredicate:
which evaluates the receiver’s content against the specified predicate and leaves only objects that match. These methods are illustrated in the following example. For more about predicates, see Predicate Programming Guide.
NSMutableArray *array = |
[NSMutableArray arrayWithObjects:@"Bill", @"Ben", @"Chris", @"Melissa", nil]; |
NSPredicate *bPredicate = |
[NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"]; |
NSArray *beginWithB = |
[array filteredArrayUsingPredicate:bPredicate]; |
// beginWithB contains { @"Bill", @"Ben" }. |
NSPredicate *sPredicate = |
[NSPredicate predicateWithFormat:@"SELF contains[c] 's'"]; |
[array filterUsingPredicate:sPredicate]; |
// array now contains { @"Chris", @"Melissa" } |
Last updated: 2010-07-08