CompactMap vs flatMap: The differences explained

CompactMap and flatMap, what are the differences and when do you use each? Swift 4.1 introduced this new method with the proposal 0187: Introduce Filtermap to gain more clarity in flatMap use cases.

When to use compactMap

Use this method to receive an array of nonoptional values when your transformation produces an optional value.

See the differences between a map and a compactMap in the following example:

let scores = ["1", "2", "three", "four", "5"]

let mapped: [Int?] = scores.map { str in Int(str) }
// [1, 2, nil, nil, 5] - Two nil values as "three" and "four" are strings.

let compactMapped: [Int] = scores.compactMap { str in Int(str) }
// [1, 2, 5] - The nil values for "three" and "four" are filtered out.

When to use flatMap

Use this method to receive a single-level collection when your transformation produces a sequence or collection for each element.

See the difference between a map and a flatMap in the following example:

let scoresByName = ["Henk": [0, 5, 8], "John": [2, 5, 8]]

let mapped = scoresByName.map { $0.value }
// [[0, 5, 8], [2, 5, 8]] - An array of arrays
print(mapped)

let flatMapped = scoresByName.flatMap { $0.value }
// [0, 5, 8, 2, 5, 8] - flattened to only one array

In fact, s.flatMap(transform) is equivalent to Array(s.map(transform).joined()).

compactMap vs flatMap

The general rule of thumb here to remind yourself for when writing code:

Used on a sequence and having a transformation returning an optional value, use compactMap. If not, either map or flatMap should give you the results you need.

The reason for naming it compactMap

Although some proposed to introduce filterMap as the method name, the Swift team decided to go for compactMap instead. Reading their motivations clears things up quite a lot and explains the functionality of the method as well.

“filterMap” is a name with some precedent in other programming languages, especially functional ones, but some people felt strongly that the name was misleading because, as a combined operation, it wasn’t quite a filter or a map. Of the alternatives, the one with the strongest support seemed to be “compactMap”, which builds on the precedent of “compact”, an operation from other languages (notably Ruby) which returns a copy of the input without nil values. Swift does not currently have such an operation, and in fact it is not currently possible to express it; nonetheless, the core team agrees that it would be a reasonable operation to add, and that “compactMap” is a good name for the operation.