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)

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


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).


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.