map, filter, compactMap, flatMap, reduce, forEach: when do you use each, and what bites?

2.1.7 · L1 · Interview

Predict first

What do these print?

print(["1", "x", "3"].compactMap(Int.init))
print([[1, 2], [3]].flatMap { $0 })
  • [1, 3] then [1, 2, 3]
  • [1, nil, 3] then [[1, 2], [3]]

Predict first

What does this print?

func find() -> Int? {
    [1, 2, 3].forEach { if $0 == 2 { return } }
    return nil
}
print(find() as Any)
  • Optional(2)
  • nil

Mental model. These are assembly-line stations: reshape, sort out, flatten, and finally boil down to one number.

map transforms every element. filter keeps some. compactMap transforms and drops nils. flatMap transforms to a sequence and joins the results into one array. reduce folds everything into a single value. forEach runs side effects and returns nothing.

Each returns a new array, so chains allocate intermediates; add .lazy when that matters. Prefer reduce(into:) when the accumulator is a collection: plain reduce hands you a fresh copy each step, which can turn a loop quadratic. And forEach is not a loop: return inside it only leaves the closure, and you cannot break.

compactMap discards the nil from "x". flatMap concatenates the inner arrays.

return inside the forEach closure leaves only that closure call. The loop carries on and find returns nil.

Common traps

  • return inside forEach. It exits that one closure call, not the enclosing function.
  • Building a dictionary with reduce. Returning a modified copy each step is O(n²). Use reduce(into:) or Dictionary(grouping:by:).

They might follow up with

  • What replaced the old optional-returning flatMap? compactMap. flatMap now only means “map to a sequence, then flatten”.
  • When is a plain for loop better? When you need break, continue, early return, or throw.

Related questions

  • 2.1.10 What does .lazy do, and what does it cost?
  • 1.2.7 What is flatMap vs map on Optional? What about Optional.map vs array map?

← What is an ArraySlice, and why does slice[0] crash?   How does sorting work in Swift, and what do you need to watch out for? →