Sorting Arrays in Swift: Tutorial & Examples

Updated: April 26, 2023 By: Khue Post a comment

Overview

Array sorting is the process of arranging elements in a specific order – either ascending or descending. Swift provides several built-in functions to sort arrays, allowing developers to write clean and efficient code. In this practical article, we’ll discuss these functions, along with custom sorting techniques and best practices for sorting arrays in Swift. We’ll also walk through various real-world examples of sorting arrays of numbers, strings, objects, and dictionaries in Swift in order from basic to advanced, from simple to complex. By the end of the day, you’ll have a solid understanding of Swift’s array sorting capabilities and best practices for implementing them in your projects.

Understanding Swift’s Built-in Sorting Functions

Swift’s built-in array sorting functions are designed for simplicity and ease of use. There are 2 primary sorting functions that you’ll need to know:

  • sorted(): This function returns a new array containing the sorted elements of the original array. It does not modify the original array.
  • sort(): This function sorts the elements of the original array in place, modifying it directly.

Both of these functions can be used with or without a custom sorting closure, allowing you to define your own sorting logic if necessary. In the following sections, we’ll explore these functions in detail and see how they can be applied to various types of arrays.

Sorting Arrays of Numbers in Swift

Ascending order & descending order

Sorting arrays of numbers is simple in Swift, thanks to the sorted() function. By default, sorted() will rearrange the elements in ascending order.

Example:

let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
let sortedNumbers = numbers.sorted()
print(sortedNumbers) 
// Output: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

If you need to sort the numbers in descending order, just pass the > (greater than) operator as a parameter:

let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
let descendingSortedNumbers = numbers.sorted(by: >)
print(descendingSortedNumbers) 
// Output: [9, 6, 5, 5, 5, 4, 3, 3, 2, 1, 1]

Adding custom sorting closure

If you need more control over the sorting process, you can provide a custom sorting closure to the sort() or sorted() functions. Let’s say you want to sort an array of integers based on their absolute values. You can achieve this by using a custom sorting closure as shown below:

let numbersWithNegatives = [3, -1, 4, -1, 5, -9, 2, 6, 5, 3, 5]
let sortedByAbsoluteValue = numbersWithNegatives.sorted { abs($0) < abs($1) }
print(sortedByAbsoluteValue) 
// Output: [-1, -1, 2, 3, 3, 4, 5, 5, 5, 6, -9]

Notice how the closure takes 2 arguments, $0 and $1, which represent two elements in the array. The closure returns true if the first element should come before the second element in the sorted order and false otherwise.

Sorting Arrays of Strings in Swift

Alphabetical Sorting

Sorting arrays of strings is just as straightforward as sorting arrays of numbers. By default, the sorted() and sort() functions will sort strings in alphabetical order:

let words = ["apple", "banana", "cherry", "date", "fig", "grape", "kiwi", "lemon", "mango", "nectarine", "orange", "papaya", "quince", "raspberry", "strawberry", "tangerine", "watermelon"]

let sortedWords = words.sorted()
print(sortedWords)

Output:

["apple", "banana", "cherry", "date", "fig", "grape", "kiwi", "lemon", "mango", "nectarine", "orange", "papaya", "quince", "raspberry", "strawberry", "tangerine", "watermelon"]

Case-Insensitive Sorting

By default, Swift’s sorting functions are case-sensitive, meaning that uppercase letters will come before lowercase letters (if you choose ascending order). To perform case-insensitive sorting, you can use the localizedCompare() function in a custom sorting closure:

import Foundation

let mixedCaseWords = ["Apple", "banana", "Cherry", "Date", "fig", "grape", "kiwi", "Lemon", "Mango", "nectarine", "orange", "Papaya", "quince", "raspberry", "STRAWBERRY", "TANGERINE", "watermelon"]

let caseInsensitiveSortedWords = mixedCaseWords.sorted { $0.localizedCompare($1) == .orderedAscending }
print(caseInsensitiveSortedWords)

Output:

["Apple", "banana", "Cherry", "Date", "fig", "grape", "kiwi", "Lemon", "Mango", "nectarine", "orange", "Papaya", "quince", "raspberry", "STRAWBERRY", "TANGERINE", "watermelon"]

Note: Don’t forget to import Foundation.

Sorting Arrays of Objects in Swift

In a case where you have an array of custom objects, you are very likely to want to sort the array based on one or some of the object’s properties. Suppose you have the following Person class:

class Person {
    let firstName: String
    let lastName: String
    let age: Int

    init(firstName: String, lastName: String, age: Int) {
        self.firstName = firstName
        self.lastName = lastName
        self.age = age
    }
}

To sort an array that contains Person objects by their age property, you can use a custom sorting closure as follows:

// SlingAcademy.com
// main.swift

class Person {
    let firstName: String
    let lastName: String
    let age: Int

    init(firstName: String, lastName: String, age: Int) {
        self.firstName = firstName
        self.lastName = lastName
        self.age = age
    }
}

// An array of Person objects
let people: [Person] = [
    Person(firstName: "Elon", lastName: "Musk", age: 55),
    Person(firstName: "Bill", lastName: "Gates", age: 69),
    Person(firstName: "John", lastName: "Doe", age: 30),
    Person(firstName: "Red", lastName: "Robot", age: 99),
    Person(firstName: "Sling", lastName: "Academy", age: 5),
]

let peopleSortedByAge = people.sorted { $0.age < $1.age }
dump(peopleSortedByAge)

Output:

▿ 5 elements
  ▿ main.Person #0
    - firstName: "Sling"
    - lastName: "Academy"
    - age: 5
  ▿ main.Person #1
    - firstName: "John"
    - lastName: "Doe"
    - age: 30
  ▿ main.Person #2
    - firstName: "Elon"
    - lastName: "Musk"
    - age: 55
  ▿ main.Person #3
    - firstName: "Bill"
    - lastName: "Gates"
    - age: 69
  ▿ main.Person #4
    - firstName: "Red"
    - lastName: "Robot"
    - age: 99

Custom Comparisons with Comparable Protocol

For more complex sorting cases such as sorting by multiple criteria, you can make your custom object conform to the Comparable protocol. This requires implementing the < (less than) operator for your object, which will be used by Swift’s sorting functions.

Continuing with the Person class example, let’s make it conform to the Comparable protocol to automatically sorts by lastName (primary criterion), then by firstName (secondary criterion):

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

    static func < (lhs: Person, rhs: Person) -> Bool {
        return lhs.lastName < rhs.lastName || (lhs.lastName == rhs.lastName && lhs.firstName < rhs.firstName)
    }
}

Complete example:

// SlingAcademy.com
// main.swift
class Person {
    let firstName: String
    let lastName: String
    let age: Int

    init(firstName: String, lastName: String, age: Int) {
        self.firstName = firstName
        self.lastName = lastName
        self.age = age
    }
}

// sort by lastName (primary criterion) and firstName (secondary criterion)
extension Person: Comparable {
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName && lhs.age == rhs.age
    }

    static func < (lhs: Person, rhs: Person) -> Bool {
        return lhs.lastName < rhs.lastName || (lhs.lastName == rhs.lastName && lhs.firstName < rhs.firstName)
    }
}

// An array of Person objects
let people: [Person] = [
    Person(firstName: "Elon", lastName: "Musk", age: 55),
    Person(firstName: "Bill", lastName: "Gates", age: 69),
    Person(firstName: "John", lastName: "Doe", age: 30),
    Person(firstName: "Red", lastName: "Robot", age: 99),
    Person(firstName: "Sling", lastName: "Academy", age: 5),
]

// Automatically sorts by lastName, then by firstName
let sortedPeople = people.sorted()

dump(sortedPeople)

Output:

▿ 5 elements
  ▿ main.Person #0
    - firstName: "Sling"
    - lastName: "Academy"
    - age: 5
  ▿ main.Person #1
    - firstName: "John"
    - lastName: "Doe"
    - age: 30
  ▿ main.Person #2
    - firstName: "Bill"
    - lastName: "Gates"
    - age: 69
  ▿ main.Person #3
    - firstName: "Red"
    - lastName: "Robot"
    - age: 99
  ▿ main.Person #4
    - firstName: "Elon"
    - lastName: "Musk"
    - age: 55

Sorting Arrays of Dictionaries in Swift

Sorting by Key Values

If you have an array of dictionaries, you might want to sort the array based on the values of a specific key. Here’s an example with an array of dictionaries representing students and their scores:

let students: [[String: Any]] = [
    ["name": "Alice", "score": 95],
    ["name": "Bob", "score": 88],
    ["name": "Charlie", "score": 92],
    ["name": "David", "score": 90],
]

let sortedByScore = students.sorted { ($0["score"] as! Int) < ($1["score"] as! Int) }
print(sortedByScore)

Output:

[
  ["name": "Bob", "score": 88], 
  ["name": "David", "score": 90], 
  ["name": "Charlie", "score": 92], 
  ["name": "Alice", "score": 95]
]

Sorting by Multiple Criteria

To sort an array of dictionaries by multiple criteria, you can use a custom sorting closure that compares the values of multiple keys:

let students: [[String: Any]] = [
    ["name": "Alice", "score": 95],
    ["name": "Bob", "score": 88],
    ["name": "Charlie", "score": 92],
    ["name": "Sling Academy", "score": 95],
    ["name": "David", "score": 90],
]

// sort by score and name (if score is the same)
let sortedByNameAndScore = students.sorted {
    if ($0["score"] as! Int) != ($1["score"] as! Int) {
        return ($0["score"] as! Int) < ($1["score"] as! Int)
    } else {
        return ($0["name"] as! String) < ($1["name"] as! String)
    }
}

print(sortedByNameAndScore)

Output:

[
  ["name": "Bob", "score": 88], 
  ["name": "David", "score": 90], 
  ["name": "Charlie", "score": 92], 
  ["name": "Alice", "score": 95], 
  ["name": "Sling Academy", "score": 95]
]

Advanced Sorting Techniques in Swift

Using Higher-Order Functions

Swift’s higher-order functions, such as map(), filter(), and reduce(), can be used in conjunction with sorting functions to perform complex sorting tasks.

For example, you can use the map() function to create a new array containing only the names of the students, and then sort that array alphabetically:

let students: [[String: Any]] = [
    ["name": "Alice", "score": 95],
    ["name": "Bob", "score": 88],
    ["name": "Charlie", "score": 92],
    ["name": "Sling Academy", "score": 95],
    ["name": "David", "score": 90],
]

// Sort array of student names in alphabetical order
let studentNames = students.map { $0["name"] as! String }.sorted()
print(studentNames) 

Output:

["Alice", "Bob", "Charlie", "David", "Sling Academy"]

Implementing Custom Sort Descriptors

For more advanced sorting tasks, you can create custom sort descriptor objects that encapsulate your sorting logic. This can be useful for sorting arrays based on multiple criteria or implementing custom sorting algorithms.

Here’s an example of a custom sort descriptor that sorts an array of Person objects by their lastName property, and then by their firstName property (this example produces the same result as one of the preceding examples, but it uses a different approach):

// SlingAcademy.com
// main.swift
class Person {
    let firstName: String
    let lastName: String
    let age: Int

    init(firstName: String, lastName: String, age: Int) {
        self.firstName = firstName
        self.lastName = lastName
        self.age = age
    }
}

struct PersonSortDescriptor {
    let keyPath: KeyPath<Person, String>
    let ascending: Bool

    func compare(_ person1: Person, _ person2: Person) -> Bool {
        let value1 = person1[keyPath: keyPath]
        let value2 = person2[keyPath: keyPath]

        return ascending ? value1 < value2 : value1 > value2
    }
}

let lastNameDescriptor = PersonSortDescriptor(keyPath: \Person.lastName, ascending: true)
let firstNameDescriptor = PersonSortDescriptor(keyPath: \Person.firstName, ascending: true)

// An array of Person objects
let people: [Person] = [
    Person(firstName: "Elon", lastName: "Musk", age: 55),
    Person(firstName: "Bill", lastName: "Gates", age: 69),
    Person(firstName: "John", lastName: "Doe", age: 30),
    Person(firstName: "Red", lastName: "Robot", age: 99),
    Person(firstName: "Sling", lastName: "Academy", age: 5),
]

let sortedPeople = people.sorted { person1, person2 in
    if lastNameDescriptor.compare(person1, person2) {
        return true
    } else if lastNameDescriptor.compare(person2, person1) {
        return false
    } else {
        return firstNameDescriptor.compare(person1, person2)
    }
}

dump(sortedPeople)

Output:

▿ 5 elements
  ▿ main.Person #0
    - firstName: "Sling"
    - lastName: "Academy"
    - age: 5
  ▿ main.Person #1
    - firstName: "John"
    - lastName: "Doe"
    - age: 30
  ▿ main.Person #2
    - firstName: "Bill"
    - lastName: "Gates"
    - age: 69
  ▿ main.Person #3
    - firstName: "Elon"
    - lastName: "Musk"
    - age: 55
  ▿ main.Person #4
    - firstName: "Red"
    - lastName: "Robot"
    - age: 99

Best Practices for Array Sorting in Swift

Some things that can be very helpful for you when dealing with array sorting stuff in Swift:

  • Use Swift’s built-in sorting functions (sorted() and sort()) whenever possible, as they are optimized for performance and readability.
  • When sorting arrays of custom objects or dictionaries, provide a custom sorting closure that clearly expresses your sorting logic.
  • Make your custom objects conform to the Comparable protocol for more complex sorting tasks or when you want to define a default sorting order.
  • Use higher-order functions (map(), filter(), and reduce()) in conjunction with sorting functions to perform more complex sorting tasks.

Final Words

Array sorting is an essential skill for any Swift developer, and by mastering the techniques discussed in this tutorial, you’ll be well-equipped to handle a variety of sorting tasks in your projects. Swift’s built-in sorting functions, along with custom sorting closures and sort descriptors, provide a powerful set of tools for sorting arrays of numbers, strings, objects, and dictionaries. Remember to follow best practices for array sorting, and don’t be afraid to experiment with different techniques to find the best solution for your specific task.

Now that you have a thorough understanding of Swift’s array sorting capabilities, it’s time to put your knowledge into practice. Try implementing some of the examples discussed in this guide, or come up with your own sorting tasks to tackle. With practice and experience, you’ll become a master of Swift’s array sorting functions in no time.

Happy coding & have a nice day!