Mobile Development 8 min read

Understanding map, flatMap, and compactMap in Swift

This article explains the differences between map, flatMap, and compactMap in Swift, covering the three overloads of flatMap, their behavior with sequences and optionals, the rationale behind renaming one overload to compactMap, and when to prefer optional chaining versus optional mapping.

Liulishuo Tech Team
Liulishuo Tech Team
Liulishuo Tech Team
Understanding map, flatMap, and compactMap in Swift

First, recall the distinction between map and flattenMap : flattenMap flattens the result of the transformation before forming the final collection. The Swift standard library provides three flatMap overloads.

Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element] where S : Sequence
Optional.flatMap<U>(_: (Wrapped) -> U?) -> U?
Sequence.flatMap<U>(_: (Element) -> U?) -> [U]

In Swift 4.1, compactMap was introduced as a renamed version of the third overload, with no functional change. Before diving into the rename, we review the various map and flatMap versions.

1. The most common Sequence.flatMap

let numbers = [1, 2, 3, 4]
let mapped = numbers.map { Array(repeating: $0, count: $0) }
// [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
// [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]

The map call returns a two‑dimensional array because the closure returns an array, while flatMap flattens each returned array into a single one‑dimensional array.

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

2. Optional.flatMap

Optional.flatMap<U>(_: (Wrapped) -> U?) -> U?

The only difference between map and flatMap for Optional lies in the return type of the transformation: map wraps the result in an extra Optional , while flatMap removes one level of Optional before wrapping.

let possibleNumber: Int? = Int("42")
let possibleSquare = possibleNumber.map { $0 * $0 }

let possibleURL: URL? = URL(string: "https://www.liulishuo.com")
let possibleHost = possibleURL.flatMap { $0.host }

In the first example, map yields an Int? because the closure returns a non‑optional value; in the second, flatMap yields an String? because the closure returns an optional.

Optional chaining can achieve similar results, but it works only when the receiver itself supports the chained property or method. For example, Int("42")?.map { $0 * $0 } does not compile because Int? has no map method.

3. The Sequence.flatMap that was renamed

Sequence.flatMap<U>(_: (Element) -> U?) -> [U]

This overload flattens an optional result into an array, which is handy for “failable” mappings:

let possibleNumbers = ["1", "2", "three", "///4///", "5"]
let flatMapped: [Int] = possibleNumbers.flatMap { str in Int(str) }
// [1, 2, 5]

If map were used instead, the result would be [Int?] containing nil values. Because the closure’s return type can be implicitly converted from Int to Int? , the compiler accepts the code, but using map would be clearer. To avoid such misuse, the overload was renamed to compactMap in Swift 4.1:

let flatMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }

4. Summary

FlatMap flattens the result of the transformation, whereas map does not.

Optional mapping vs. optional chaining differ in where the optional handling occurs and in expressiveness.

The Sequence.flatMap that returns an array from an optional was renamed to compactMap to discourage accidental misuse.

5. Review

Click to review 👉 Swift 4.1 New Feature 1: Conditional Conformance

iosSwiftFunctional ProgrammingMapOptionalFlatMapcompactMap
Liulishuo Tech Team
Written by

Liulishuo Tech Team

Help everyone become a global citizen!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.