map, filter & reduce
Reviewed & published by Brayan K
Higher-order methods transform collections without writing loops. You'll learn map, filter, reduce, compactMap, and sorted — the everyday tools of expressive, functional Swift.
Part of the free Swift course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Transform every element with map
- • Keep matching elements with filter
- • Fold a collection into one value with reduce
- • Transform-and-drop-nils with compactMap
- • Order elements with sorted and sorted(by:)
- • Chain these methods into clear data pipelines
1️⃣ map — Transform Every Element
map runs your closure on every element and collects the results into a new array of the same length. It never changes the original.
// map transforms EACH element and returns a NEW array of the same length.
let numbers = [1, 2, 3, 4]
// Double every number:
let doubled = numbers.map { $0 * 2 }
print(doubled) // [2, 4, 6, 8]
// Turn numbers into strings:
let labels = numbers.map { "#\($0)" }
print(labels) // ["#1", "#2", "#3", "#4"]
// The original array is untouched:
print(numbers) // [1, 2, 3, 4]2️⃣ filter — Keep What Matches
filter keeps only the elements for which your closure returns true, producing a (usually shorter) new array.
// filter keeps only the elements for which the closure returns true.
let scores = [42, 78, 91, 55, 88]
// Keep the passing scores (>= 60):
let passing = scores.filter { $0 >= 60 }
print(passing) // [78, 91, 88]
// Keep the even numbers:
let evens = [1, 2, 3, 4, 5, 6].filter { $0 % 2 == 0 }
print(evens) // [2, 4, 6]3️⃣ reduce — Fold Into One Value
reduce combines a collection into a single value. The first argument is the starting value; the closure merges the running result with each next element.
// reduce combines ALL elements into a single value.
// First argument = starting value; closure combines running result + next.
let prices = [3, 5, 2, 4]
// Sum them up, starting from 0:
let total = prices.reduce(0) { runningTotal, next in
runningTotal + next
}
print(total) // 14
// The same, with shorthand arguments:
let total2 = prices.reduce(0) { $0 + $1 }
print(total2) // 14
// reduce isn't only for sums — find the max starting from the first guess:
let longestWord = ["hi", "hello", "hey"].reduce("") {
$1.count > $0.count ? $1 : $0
}
print(longestWord) // hello4️⃣ compactMap & sorted
compactMap transforms then drops any nil results — ideal for parsing. sorted() returns a new ordered array (ascending by default), and sorted(by:) lets you choose the order.
// compactMap transforms AND removes any nil results.
let inputs = ["1", "two", "3", "4x", "5"]
let parsed = inputs.compactMap { Int($0) }
print(parsed) // [1, 3, 5] — "two" and "4x" became nil and were dropped
// sorted() returns a NEW sorted array (the original is unchanged).
let messy = [3, 1, 4, 1, 5]
print(messy.sorted()) // [1, 1, 3, 4, 5] (ascending)
print(messy.sorted(by: { $0 > $1 })) // [5, 4, 3, 1, 1] (descending)
print(messy) // [3, 1, 4, 1, 5] (untouched)Your turn. Fill in the blanks to filter and then reduce.
// 🎯 YOUR TURN — fill in the blanks, then run it in SwiftFiddle.
let nums = [10, 15, 20, 25]
// 1) Keep only the values greater than 12. Replace ___ with the method name.
let big = nums.___ { $0 > 12 } // 👉 replace ___ with 'filter'
print(big) // [15, 20, 25]
// 2) Sum every value. Replace ___ with the starting value for reduce.
let sum = nums.reduce(___) { $0 + $1 } // 👉 replace ___ with 0
print(sum) // 70Pro Tips
- 💡 Chain them. data.filter { … }.map { … } reads as a pipeline.
- 💡 Use compactMap for parsing. It unwraps and drops failures in one step.
- 💡 Prefer reduce over manual accumulators for sums and joins.
- 💡 Remember they're non-mutating. Capture the result in a new constant.
Common Errors (and the fix)
- "Result of call to 'map' is unused" — these methods return a value; assign it to a constant.
- Expecting map to remove nils — it doesn't; use compactMap to drop nil results.
- "Cannot convert value of type" in reduce — the running result and the initial value must be the same type.
- Mutating the original after sorted() — sorted() doesn't change the source; use sort() on a var for in-place sorting.
📋 Quick Reference
| Method | Example | Result |
|---|---|---|
| map | [1,2].map { $0*2 } | [2, 4] |
| filter | [1,2,3].filter { $0>1 } | [2, 3] |
| reduce | [1,2,3].reduce(0,+) | 6 |
| compactMap | ["1","x"].compactMap { Int($0) } | [1] |
| sorted | [3,1].sorted() | [1, 3] |
Mini-Challenge: Shopping List Pipeline
No blanks this time — just a brief and an outline. Build it, run it, and check your output against the example in the comments.
// 🎯 MINI-CHALLENGE: process a shopping list
// Given: let cart = ["2", "five", "3", "10"]
// 1. Use compactMap to turn the parseable strings into Ints.
// 2. Use filter to keep values >= 3.
// 3. Use reduce to sum them.
// 4. Print the final total.
//
// ✅ Expected output (kept: 3 and 10):
// 13
// your code here🎉 Lesson Complete!
- ✅ map transforms each element (same length)
- ✅ filter keeps matching elements
- ✅ reduce folds everything into one value
- ✅ compactMap transforms and drops nils
- ✅ sorted() returns a new ordered array
Practice quiz
What does map(_:) do?
- Removes elements that fail a test
- Combines all elements into one value
- Transforms each element, returning a new array of the same length
- Sorts the array
Answer: Transforms each element, returning a new array of the same length. map transforms every element with a closure and returns a new array of the same length.
What does filter(_:) return?
- A new array containing only elements that pass the test
- A single value
- The first matching element
- A Boolean
Answer: A new array containing only elements that pass the test. filter keeps only the elements for which the closure returns true, in a new array.
What does reduce(_:_:) produce?
- A new array
- A sorted array
- An optional
- A single combined value from all elements
Answer: A single combined value from all elements. reduce combines all elements into a single accumulated value, starting from an initial value.
What is the first argument to reduce?
- The closure
- The initial (starting) value
- The array length
- A Bool
Answer: The initial (starting) value. reduce takes an initial value first, then a combining closure: reduce(0) { $0 + $1 }.
What does compactMap(_:) do that map does not?
- It removes nil results, unwrapping the rest
- It sorts the result
- It reverses the array
- It throws on nil
Answer: It removes nil results, unwrapping the rest. compactMap transforms then drops any nil results, returning an array of unwrapped non-optional values.
What does [3, 1, 2].sorted() return?
- [3, 2, 1]
- [2, 1, 3]
- [1, 2, 3]
- It mutates in place and returns nothing
Answer: [1, 2, 3]. sorted() returns a NEW array in ascending order: [1, 2, 3]. The original is unchanged.
How do you sort descending with sorted(by:)?
- sorted(by: { $0 < $1 })
- sorted(by: { $0 > $1 })
- sorted(descending: true)
- sorted().reverse()
Answer: sorted(by: { $0 > $1 }). Passing { $0 > $1 } sorts in descending order.
What is [1, 2, 3].map { $0 * 2 } ?
- [1, 2, 3]
- 6
- [1, 4, 9]
- [2, 4, 6]
Answer: [2, 4, 6]. map doubles each element: [2, 4, 6].
Do map, filter, and reduce mutate the original collection?
- Yes, all of them
- No, they each return a new value and leave the original unchanged
- Only filter mutates
- Only reduce mutates
Answer: No, they each return a new value and leave the original unchanged. These higher-order methods are non-mutating; they return new results and leave the source intact.
What is ['1', 'x', '3'].compactMap { Int($0) } ?
- [1, nil, 3]
- [1, 0, 3]
- [1, 3]
- It crashes
Answer: [1, 3]. Int('x') is nil, which compactMap drops, leaving [1, 3].
Continue this course
- Previous: Closures in Depth
- Next: Protocols — Define requirements as contracts and program to interfaces
Frequently asked questions
What is the difference between map and filter?
map transforms every element and always returns a new array of the same length — one output per input. filter tests each element and returns a new array containing only the elements that passed, so the result is usually shorter. Neither changes the original collection.
How does reduce work in Swift?
reduce(_:_:) folds a whole collection into a single value. You give it a starting value and a closure that combines the running result with the next element. For example [1,2,3].reduce(0) { $0 + $1 } produces 6. It's perfect for sums, products, joining strings, or finding a maximum.
When should I use compactMap instead of map?
Use compactMap when your transformation returns optionals and you want to drop the nils. For example, ['1','x','3'].compactMap { Int($0) } gives [1, 3] — the failed conversion is removed and the rest are unwrapped. Plain map would leave you with [Optional(1), nil, Optional(3)].
Does sorted() change the original array?
No. sorted() (and sorted(by:)) return a brand-new sorted array and leave the original untouched. If you want to sort in place and mutate the array, use the mutating sort() method instead, which requires the array to be a var.
Are map, filter, and reduce slower than a for loop?
In practice they are typically as fast as an equivalent loop and often clearer to read. The Swift compiler optimizes them well. Chaining them (map then filter) creates intermediate arrays, so for very large data or tight loops you can use lazy or combine steps — but for everyday code, prefer readability.