Part of Swift with Copilot

Swift with Copilot: Sort list ascending and descending

Sandy LaneSandy Lane

Video: Swift with Copilot: Sort list ascending and descending by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Swift with Copilot: Sort List Ascending and Descending

arr.sorted() ascending. arr.sorted(by: >) descending. The by: parameter takes any (a, b) -> Bool<, >, or your own custom comparator.

Same data, two orders. Swift makes both one-liners.

The Copilot prompt

// Sort numbers ascending and descending
let numbers = [5, 2, 8, 1, 9, 3]

Copilot completes:

let numbers = [5, 2, 8, 1, 9, 3]

let ascending = numbers.sorted()
let descending = numbers.sorted(by: >)

print("Asc: \(ascending)")
print("Desc: \(descending)")
// Asc: [1, 2, 3, 5, 8, 9]
// Desc: [9, 8, 5, 3, 2, 1]

Walkthrough

sorted(by: <closure>) takes a closure (a, b) -> Bool that answers "should a come before b?"

arr.sorted(by: <)    // ascending
arr.sorted(by: >)    // descending

Operators in Swift are functions. < and > work directly as the by: argument because they have type (Int, Int) -> Bool.

For a custom comparator, write a closure:

let absSorted = numbers.sorted { abs($0) < abs($1) }
// sort by absolute value

$0 and $1 are the two arguments. The closure returns true if $0 should come before $1.

Sort in place, descending

var numbers = [5, 2, 8, 1, 9, 3]
numbers.sort(by: >)
print(numbers)
// [9, 8, 5, 3, 2, 1]

Same by: form on the in-place version.

Reversed: another way to descending

let descending = numbers.sorted().reversed()
print(Array(descending))
// [9, 8, 5, 3, 2, 1]

reversed() returns a ReversedCollection — a lightweight view, not a new array. Use Array(...) to materialize. For one-pass iteration, no need.

sorted(by: >) is more direct than sorted().reversed().

Sort strings

let names = ["Charlie", "Alice", "Bob"]
let sorted = names.sorted()
// ["Alice", "Bob", "Charlie"]

Lexicographic, case-sensitive. For case-insensitive:

let sortedCI = names.sorted { $0.lowercased() < $1.lowercased() }

Sort objects by a property

struct Person {
  let name: String
  let age: Int
}

let people = [
  Person(name: "Alice", age: 30),
  Person(name: "Bob", age: 25),
  Person(name: "Charlie", age: 35),
]

// By age
let byAge = people.sorted { $0.age < $1.age }

// By name
let byName = people.sorted { $0.name < $1.name }

// By age descending
let byAgeDesc = people.sorted { $0.age > $1.age }

For a more reusable pattern, conform to Comparable:

extension Person: Comparable {
  static func < (lhs: Person, rhs: Person) -> Bool {
    lhs.age < rhs.age
  }
  static func == (lhs: Person, rhs: Person) -> Bool {
    lhs.age == rhs.age && lhs.name == rhs.name
  }
}

people.sorted()    // by age, no need for closure

Useful when the type has a "natural" order.

Multi-key sort (tiebreaker)

let byAgeThenName = people.sorted {
  if $0.age != $1.age { return $0.age < $1.age }
  return $0.name < $1.name
}

Or chain with tuples:

let sorted = people.sorted { ($0.age, $0.name) < ($1.age, $1.name) }

Tuples are Comparable if their elements are. Lexicographic compare: age first, name as tiebreaker.

KeyPath sorting (Swift 5.0+)

For one-key sorts, KeyPaths are cleaner:

let byAge = people.sorted(using: KeyPathComparator(\.age))

KeyPathComparator is from Foundation (iOS 15+, macOS 12+).

For older code, write an extension:

extension Sequence {
  func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>) -> [Element] {
    sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
  }
}

let byAge = people.sorted(by: \.age)
let byName = people.sorted(by: \.name)

Now \.age (a KeyPath) replaces a closure. Compact and reusable.

Common stumbles

sorted(by: <) vs sorted(). Both ascending. sorted() works because the default uses <.

Forgetting to materialize reversed(). let r = arr.reversed() is a view, not Array. Array(r) to copy.

Sorting nils. [1, nil, 3].sorted() errors because Optional doesn't conform to Comparable directly. Filter or write a custom comparator.

Comparator returning wrong type. Must return Bool. sorted { $0 - $1 } errors — that's Int.

Not stable. If preserving relative order of equals matters, multi-key sort with a tiebreaker.

What's next

Episode 5: What day is today? Date, Calendar, DateFormatter.

Recap

sorted(by: <) ascending (= sorted()), sorted(by: >) descending. Pass any (a, b) -> Bool closure for custom orders. For object arrays, sort by closure on a property or conform to Comparable. Multi-key via tuple comparison. KeyPathComparator (newer SDKs) for keypath-based sorts.

Next episode: today's date.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.