Add sort overload to Sequence to sort by a Comparable attribute of elements

Sorting sequences is a really common operation. Swift's Sequence type makes this easy with the sorted(by:) method. I think this could be streamlined a little with an overload that takes a transform from Element to a Comparable:

extension Sequence {
    func sortedBy<T: Comparable>(_ transform: (Self.Element) throws -> T) rethrows -> [Self.Element] {
        return try self.sorted { (lhs, rhs) -> Bool in
            let l = try transform(lhs)
            let r = try transform(rhs)
            return l < r
        }
    }
}

This makes sorting a sequence by one attribute of its elements a little more straightforward. Given a sequence of Person structs with firstName and lastName properties, sorting by lastName currently requires:

let sorted = people.sortedBy({$0.lastName < $1.lastName})

With this additional overload, we can simply write:

let sorted = people.sortedBy({$0.lastName})

Addding sortedByDescending would allow sorting either way.

If we add this to the Sorted Collections idea and take a cue from C#'s similar API, we could add .thenBy(transform) and .thenByDescending(transform) and can sort by last and then first name with:

let sorted = people.sortedBy({$0.lastName}).thenBy({$0.firstName})

IMO this reads more clearly than implementing the two-tier sorting with a comparator:

let sorted = people.sorted { (lhs, rhs) -> Bool in
    return (lhs.lastName == rhs.lastName ? lhs.firstName < rhs.firstName : lhs.lastName < rhs.lastName)
    }
2 Likes

Hi! Something like this has been discussed several times. You might find the following pitch to be edifying:

Similarly:

In the latter thread, @Ben_Cohen (from the core team) writes:

I think there is a lot of room for ergonomic improvements to sorting, so it's great to see this!

I would suggest, though, taking a step back and thinking about all the different aspects of this as a a whole. Individual targeted fixes are nice, but risk API sprawl when they handle some cases not others, leading to other targeted fixes.

Some topics to consider:

  • key paths vs closures
    • note, localized/caseless sorting is also painful, but at least doable, with closure-based arguments
    • including weighing the pros/cons of parametrizing with ascending/descending choice (not a technique that sits well with the existing idioms used in the std lib)
  • sorting by a sequence of comparators, lexicographically
  • partial sorting/partitioning

A great place to start would be a comparison with the existing APIs to be found in other places i.e. Java, Ruby, Cocoa (which has all sorts of different ways to sort, sortUsingDescriptors being particularly of interest).

5 Likes

Ah, thanks! I searched briefly for existing pitches but didn't find those.

This would definitely be nice, I often make versions of this. I think it makes sense even if the entire sort system is overhauled, we will always want this.