Skip to main content
2 of 2
replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/

What if our array has strings in it? Does ascending still make sense? Maybe. Does it mean alphabetically ascending? Or does it mean ascending in length?

What if our array holds CLLocation objects? Are we sorting by latitude ascending? Longitude ascending? Altitude ascending? Distance from (0,0) ascending?

What if our array holds NSArray objects? What if it holds NSDictionary objects?

There are a plethora of problems here. Your formula, first of all, as Martin R's answer points out, is flawed in that it is comparing pointer values and not the actual contents of any of the objects in its array.

The naïve fix is to change your < operators to use the compare: method. This also has problems. If we're not using Xcode 7+ or if we want to use any legacy array, we can't use Objective-C generics, so we can't depend on some sort of particular object being in the array, nor can we rely on all of the objects in the array conforming to a protocol that declares the compare: method. What's worse, if we rely on compare:, we box ourselves in to a particular implementation. How do you, for example, use compare: on CLLocation objects? What if you want to sort some CLLocation objects based on distance from a particular point? Or what if you want to sort NSDate objects based on time since a particular point in time (3 minutes in the future is closer to now than 5 minutes in the past, but using compare will result in 5 minutes in the past being sorted before 3 minutes in the future).

This is exactly why the Apple sorting methods expect a "comparator" block argument. The sorting method only cares about the algorithm itself, and it relies on the result of running two objects through the comparator to determine the order they should be sorted in.

So, for example, if dateArray is an array of NSDate objects, and I want to sort it from earliest to latest date, I'd do something like this (using Apple's built-in method):

NSArray *sortedDates = [dateArray sortedArrayUsingComparator:^(id a, id b) {
    return [a compare:b];
}];

If I want to sort in the reverse order, I can do the same and simply flop a and b:

NSArray *sortedDates = [dateArray sortedArrayUsingComparator:^(id a, id b) {
    return [b compare:a];
}];

Or if I want to do something trickier, and sort them based on how far they are from now, I can also do that (still using the same method):

NSArray *sortedDates = [dateArray sortedArrayUsingComparator:^(id a, id b) {
    NSTimeInterval aFromNow = abs(a.timeIntervalSinceNow);
    NSTimeInterval bFromNow = abs(b.timeIntervalSinceNow);

    if (a < b) {
        return NSOrderedAscending;
    } else if (b < a) {
        return NSOrderedDescending;
    } else {
        return NSOrderedSame;
    }
}];

And now without modifying the method or algorithm or making any assumptions about what or how the caller is sorting, my algorithm works for sorting their array in the way they wanted it sorted.

All you have to do internally is compare the two objects you're trying to sort using the comparator block that has been passed in. That will tell you in which order they belong, and will allow you to sort things in a million different ways with the same algorithm. And it's type-agnostic--it can sort anything.

nhgrif
  • 25.3k
  • 3
  • 63
  • 129