map, filter, and reduce
Prerequisites: Closures
These three methods, each taking a closure, replace most hand-written loops that build new collections.
map — transform every element
let prices = [10.0, 20.0, 30.0]
let withTax = prices.map { $0 * 1.2 }
print(withTax)
let names = ["ada", "linus"]
print(names.map { $0.uppercased() })
print(names.map(\.count))
[12.0, 24.0, 36.0]
["ADA", "LINUS"]
[3, 5]
\.count is a key path — a shorthand for { $0.count }.
filter — keep matching elements
let nums = Array(1...10)
print(nums.filter { $0 % 2 == 0 })
print(nums.filter { $0 > 7 }.count)
[2, 4, 6, 8, 10]
3
reduce — combine into one value
reduce starts with an initial value and combines each element into
it:
let nums = [1, 2, 3, 4]
let total = nums.reduce(0) { sum, n in sum + n }
print(total)
print(nums.reduce(0, +))
print(nums.reduce(1, *))
10
10
24
compactMap — transform and drop the nils
let inputs = ["1", "two", "3", ""]
let numbers = inputs.compactMap { Int($0) }
print(numbers)
[1, 3]
compactMap is the everyday way to convert a list where some items
might fail.
Chaining
let orders = [120.0, 45.0, 300.0, 80.0]
let bigOrdersTotal = orders
.filter { $0 > 100 }
.map { $0 * 0.9 }
.reduce(0, +)
print(bigOrdersTotal)
378.0
Common mistakes
- Using
mapwhen the closure returns an optional and you want to drop failures — that gives an array of optionals. UsecompactMap. - Using
forEachto build a new array.mapandfiltersay what you mean directly.
Practice
Given let names = ["ada", "linus", "grace", "al"], produce an array of
the names longer than 2 letters, capitalized.
Hint: "ada".capitalized needs import Foundation.
Show solution
import Foundation
let names = ["ada", "linus", "grace", "al"]
print(names.filter { $0.count > 2 }.map { $0.capitalized })
["Ada", "Linus", "Grace"]