Chapter 68: Swift Sorting
1. The Big Picture: What does “sorting” mean in Swift?
Sorting means rearranging the elements of a collection so they appear in a specific order.
Most common orders people want:
- Ascending (small → big): 1, 3, 7, 12, 25
- Descending (big → small): 25, 12, 7, 3, 1
- Alphabetical (A → Z): Apple, Banana, Mango, Orange
- Reverse alphabetical (Z → A)
- Custom order (e.g. by priority, by distance, by recency, by user rating…)
Swift gives you very elegant and type-safe ways to sort.
The two most important methods you will use every single day:
- sorted() → returns a new sorted array (original unchanged)
- sort() → sorts the array in place (modifies the original)
2. The simplest & most common cases
Case 1 – Sorting numbers (ascending by default)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
let scores = [78, 92, 65, 88, 45, 95, 72] // Returns a NEW sorted array (original unchanged) let sortedAscending = scores.sorted() print(sortedAscending) // [45, 65, 72, 78, 88, 92, 95] // Sort in place (modifies original array) var mutableScores = scores mutableScores.sort() print(mutableScores) // [45, 65, 72, 78, 88, 92, 95] |
Descending order (very common):
|
0 1 2 3 4 5 6 7 8 9 |
let descending = scores.sorted(by: >) // [95, 92, 88, 78, 72, 65, 45] // or mutableScores.sort(by: >) |
Real tip: > is actually a function (Int, Int) -> Bool — that’s why it works directly.
Case 2 – Sorting strings (alphabetical)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
let names = ["Rahul", "Priya", "Aarav", "Sneha", "Karan", "Meera"] let alphabetical = names.sorted() // ["Aarav", "Karan", "Meera", "Priya", "Rahul", "Sneha"] let reverseAlpha = names.sorted(by: >) // ["Sneha", "Rahul", "Priya", "Meera", "Karan", "Aarav"] |
Case-insensitive sorting (very important in real apps):
|
0 1 2 3 4 5 6 7 8 9 |
let mixedCase = ["apple", "Banana", "cherry", "Date", "apricot"] let caseInsensitive = mixedCase.sorted { $0.lowercased() < $1.lowercased() } // ["apple", "apricot", "Banana", "cherry", "Date"] |
3. Sorting custom types (structs / classes) — this is where it gets really useful
Almost every real app needs to sort objects, not just numbers or strings.
Step 1 – Make your type comparable (the cleanest way)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
struct Student { let name: String let score: Int let age: Int } // Make it Comparable — Swift will use < operator extension Student: Comparable { static func < (lhs: Student, rhs: Student) -> Bool { lhs.score < rhs.score // sort by score ascending // You can add more rules: // lhs.score != rhs.score ? lhs.score < rhs.score : lhs.age < rhs.age } } let students = [ Student(name: "Rahul", score: 920, age: 19), Student(name: "Priya", score: 980, age: 18), Student(name: "Aarav", score: 850, age: 20), Student(name: "Sneha", score: 950, age: 17) ] // Now you can sort naturally let sortedByScore = students.sorted() print("Sorted by score (ascending):") for s in sortedByScore { print(" \(s.name) - \(s.score) points, age \(s.age)") } |
Output:
|
0 1 2 3 4 5 6 7 8 9 10 |
Sorted by score (ascending): Aarav - 850 points, age 20 Rahul - 920 points, age 19 Sneha - 950 points, age 17 Priya - 980 points, age 18 |
Step 2 – Sort by multiple criteria (very common)
|
0 1 2 3 4 5 6 7 8 9 10 11 |
let sortedByScoreThenAge = students.sorted { a, b in if a.score != b.score { return a.score > b.score // higher score first } return a.age < b.age // if scores equal → younger first } |
Shorter version (using tuple comparison):
|
0 1 2 3 4 5 6 7 8 |
let sorted = students.sorted { ($0.score, $0.age) > ($1.score, $1.age) // descending score, then ascending age } |
4. Real-life examples you will actually write
Example 1 – Leaderboard / high scores (games, quizzes)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
struct ScoreEntry { let player: String let points: Int let date: Date } let leaderboard = [ ScoreEntry(player: "Rahul", points: 920, date: Date()), ScoreEntry(player: "Priya", points: 980, date: Date()), ScoreEntry(player: "Aarav", points: 850, date: Date()), ScoreEntry(player: "Sneha", points: 950, date: Date()) ] // Sort descending by points, then by date (newest first if tie) let ranked = leaderboard.sorted { a, b in if a.points != b.points { return a.points > b.points } return a.date > b.date } print("🏆 Leaderboard") for (position, entry) in ranked.enumerated() { let rank = position + 1 let rankSymbol = rank == 1 ? "🥇" : rank == 2 ? "🥈" : rank == 3 ? "🥉" : "\(rank)." print("\(rankSymbol) \(entry.player) - \(entry.points) pts") } |
Example 2 – Sort products by price, then by name
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
struct Product { let name: String let price: Double } let products = [ Product(name: "Earbuds Pro", price: 24999), Product(name: "Phone Case", price: 999), Product(name: "Charger", price: 2499), Product(name: "Earbuds", price: 9999) ] let sortedProducts = products.sorted { a, b in if a.price != b.price { return a.price < b.price // cheapest first } return a.name < b.name // if price equal → alphabetical } print("Products sorted by price, then name:") for p in sortedProducts { print(" ₹\(String(format: "%.0f", p.price)) – \(p.name)") } |
5. Very Common Beginner Mistakes & Fixes
| Mistake | Wrong / Dangerous code | Correct / Better habit | Why? |
|---|---|---|---|
| Mutating array while sorting | array.sort { … } then keep using old reference | let sorted = array.sorted { … } | sort() mutates, sorted() returns new |
| Force-unwrapping inside sort closure | sorted { $0!.score < $1!.score } | Use compactMap first or optional chaining | Crash if any nil |
| Sorting optional values without handling nil | sorted { $0 < $1 } on [Int?] | sorted { $0 ?? 0 < $1 ?? 0 } or filter nils | nil crashes comparison |
| Wrong comparator direction | sorted { $0.score < $1.score } for descending | sorted { $0.score > $1.score } | Easy to mix up ascending/descending |
| Not using sorted(by:) when logic is complex | sorted() on custom type | sorted { a, b in … } with clear rules | Default Comparable may not be what you want |
6. Quick Reference – Sorting cheat sheet
| Goal | Most idiomatic code | Notes / Tip |
|---|---|---|
| Numbers ascending | array.sorted() or array.sorted(by: <) | Default is ascending |
| Numbers descending | array.sorted(by: >) | Very common |
| Strings case-insensitive | sorted { $0.lowercased() < $1.lowercased() } | Handles “Apple” vs “banana” correctly |
| Custom struct by one property | sorted { $0.score > $1.score } | Descending score |
| Custom struct by multiple properties | sorted { ($0.score, $0.age) > ($1.score, $1.age) } | Score descending, then age ascending |
| Keep original unchanged | let sorted = array.sorted { … } | sorted() returns new array |
| Sort in place (modify original) | array.sort { … } | Use only when you don’t need the old order |
7. Small Practice – Try these
- Create array of 6 numbers → Sort them ascending → Sort them descending
- Create array of 5 names (mixed case) → Sort them alphabetically (case-insensitive)
- Create array of structs: struct Product { let name: String; let price: Double } → Sort by price ascending, then by name alphabetical if prices equal
Paste your code here if you want feedback or want to see even cleaner versions!
What would you like to explore next?
- Sorting with multiple criteria in more depth
- Sorting custom types with Comparable conformance
- Sorting in SwiftUI (sorted lists, @State updates)
- Array slicing & memory behavior
- Collections in SwiftUI (List, ForEach, diffable data sources)
- Or move to another topic (dictionaries, sets, optionals, switch…)
Just tell me — we’ll continue in the same clear, detailed, patient style 😊
