Functional Programming in Swift
TrailLog's statistics screen needs total distance, the names of long hikes, and the average elevation. You could write three loops with counters and temporary arrays. Or you can describe what you want and let Swift's collection functions do the looping.
The rule
Transform with
map, keep withfilter, combine withreduce.
These three higher-order functions take a closure and return a new value, leaving the original collection untouched:
let journal = [
Hike(name: "Ridge Loop", distanceKm: 8.5),
Hike(name: "Summit Push", distanceKm: 14.0),
Hike(name: "River Walk", distanceKm: 3.0)
]
let names = journal.map { $0.name }
// ["Ridge Loop", "Summit Push", "River Walk"]
let long = journal.filter { $0.distanceKm > 5 }
// Ridge Loop and Summit Push
let total = journal.reduce(0) { sum, hike in sum + hike.distanceKm }
// 25.5map— same number of items out as in, each transformed.filter— fewer (or equal) items out, each unchanged, kept when the closure returnstrue.reduce(initial, combine)— many items in, one value out. Start frominitialand fold each item in.compactMap— likemap, but dropsnilresults:["12", "x", "7"].compactMap { Int($0) }is[12, 7].
The functional style prefers immutable values (let) and pure functions — functions whose output depends only on their input and that change nothing else. Pure code is easy to test and safe to run from any thread.
These calls chain naturally, each step feeding the next:
let longNames = journal
.filter { $0.distanceKm > 5 }
.map { $0.name }
.sorted()
// ["Ridge Loop", "Summit Push"]The shortcut
Compare the count going in with the count coming out: same count, changed items → map; fewer items, unchanged → filter; one value → reduce.
Keep the long hikes
Fewer items out, unchanged →
filter { $0.distanceKm > 5 }gives two hikes.
The trap: using map when the closure returns an optional, then getting an array full of nils. If some results may be missing, use compactMap.
Drills
Answer fast — trust the rule.
Which function turns [Hike] into [String] of names?
What is [2, 5, 8].reduce(0) { $0 + $1 }?
What is ["3", "hill", "9"].compactMap { Int($0) }?
Which is a pure function?
Remember
- Transform with
map, keep withfilter, combine withreduce;compactMapdropsnils. - Shortcut: compare the count in with the count out.
- Prefer immutable values and pure functions — easier to test, safe on any thread.
