 # FlatMap and CompactMap Explained in Swift

Written by LearnAppMaking on July 12 2020 in App Development, Swift Swift has a bunch of functions that are useful for transforming collections and sequences. In this tutorial, we’ll discuss `map(_:)`, `flatMap(_:)` and `compactMap(_:)`.

Here’s what we’ll focus on:

1. How `map(_:)` transforms a collection or sequence by applying a closure to it
2. How `flatMap(_:)` can flatten an input array, after calling `map(_:)`
3. How `compactMap(_:)` removes `nil` from the input array

In a previous tutorial, we’ve discussed how you can use `filter(_:)` and `reduce(_:)`. These higher-order functions are super helpful for transforming collections, in a concise and insightful way.

Not yet familiar with closures or higher-order functions? Make sure to read up on those first:

## Using the Map Function in Swift

As a quick refresher on higher-order functions in Swift, our starting point is the `map(_:)` function. This function applies a transformation to each of the elements in a sequence, like an array or dictionary.

Here’s an example:

Let’s break that down:

First, we’re creating an array `numbers` with a few integer values. Then, the function `map(_:)` is called on `numbers` and its result is assigned to `result`.

The `map(_:)` function has one parameter, a closure, which returns the result of `\$0 * \$0`. The `\$0` corresponds to the first parameter of the closure, i.e. the number from `numbers` that’s being transformed.

We’re calculating the square of every number in `numbers`. In essence, the operation `\$0 * \$0` is called on every number in `numbers`, and the resulting array is assigned to `result`. You’re transforming – or “mapping” – one array into another.

Transforming an array with `map(_:)` is similar to using a for loop, but much more concise. Like this:

``````let numbers = [2, 3, 4, 5]
var result = [Int]()

for number in numbers {
result += [number * number]
}

print(result)
``````

Here’s another way to look at it. With `map(_:)`, the input array of numbers is transformed into another array of numbers. Like this:

``````2 => 2 * 2 => 4
3 => 3 * 3 => 9
4 => 4 * 4 => 16
5 => 5 * 5 => 25
``````

Functions like `map(_:)` are called higher-order functions, because they take a function as input as opposed to ordinary values. Higher-order functions can also output functions, which is useful for a programming paradigm called functional programming.

Technically, you can call higher-order functions like `map(_:)` on any sequence. This includes collections like arrays, dictionaries, and sets, ranges like `1...100` and so-called iterators. Anything that looks like a “list” of values, basically.

We’ll discuss why higher-order functions are useful at the end of this tutorial. Let’s first move on to learn about `flatMap(_:)` and `compactMap(_:)`.

## Using the FlatMap Function

The `flatMap(_:)` function is similar to `map(_:)` except that it “flattens” the resulting array. Here’s an example:

The above code starts out with a nested array of integers. The `numbers` array consists of an array of 3 arrays, that each contains 3 numbers.

The closure `{ \$0 }` simply returns the first argument of the closure, i.e. the individual nested arrays. No transformation or operation is happening. When you call `flatMap(_:)` on the `numbers` array though, instead of `map(_:)`, you end up with a flattened array of individual numbers. Unlike the input array `numbers`, the result array doesn’t contain nested arrays!

Let’s look at another example. Imagine you’re working with 4 groups of giraffes, and want to create one single group of giraffes that are taller than a certain height. Here’s how you do that:

See how `giraffes` contains an array of arrays? In the above code, the function `filter(_:)` is called on every nested array inside `giraffes`. We only want integers (giraffes!) that are greater than `10`. The resulting arrays are flattened into one “flat” array, and assigned to `tallest`.

Consider what would happen if we had used `map(_:)` instead of `flatMap(_:)`. The resulting array wouldn’t be flattened. Instead, it’d be this:

``````[[], [11, 13, 20], ]
``````

It’s important to note that the `flatMap(_:)` function calls `map(_:)` on the array items first, and then flattens it. That’s why something like the following doesn’t work:

``````let numbers = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let result = numbers.flatMap({ \$0 * 2 })
``````

In the above code, the `\$0` refers to the arrays inside numbers. Multiplying an array by two is impossible, so that’s why this code doesn’t work.

It’s smart to think about flatmapping as seeing an array in one less dimension. You start with a two-dimensional array, and end up with a one-dimensional array after `flatMap(_:)`.

What about using `flatMap(_:)` with optionals? Let’s look at that next.

The name “compact map” is based on the idea that removing `nil` items from an array makes the array more compact. Likewise, the name “flat map” comes from flattening the array. And “mapping” is a concept from mathematics, where you associate the values in one set with another set.

## Using the CompactMap Function

The `compactMap(_:)` function removes `nil` values from the input array. It’s super useful when working with optionals.

Before Swift 4.1, the `flatMap(_:)` function (above) could also be used to filter out `nil` values from flattened arrays. Since Swift 4.1+, you now use the explicit `compactMap(_:)` for this purpose.

Here’s an example:

See what happens? The most important part of the code is `Int(\$0)`. This takes an individual string from `numbers` with `\$0` and attempts to convert to an integer, with the `Int()` initializer.

This `Int()` initializer is failable: it can return `nil` – an optional – so its return type is `Int?`. As a result, the return type of the mapping transformation is `[Int?]` – an array of optional integers.

``````[Optional(5), Optional(42), nil, Optional(100), nil]
``````

The `compactMap(_:)` function automatically removes `nil` elements from the returned array, after calling `map(_:)` on it. As such, it’s return type is non-optional.

``````[5, 42, 100]
``````

In the above code, the type of `result` is `[Int]`. If you would have used `map(_:)`, the return type would have been `[Int?]`. And you would have needed an extra step to unwrap the values from the array, to work with them.

## Why Use FlatMap and CompactMap?

Before we discuss the real-world use cases of flatmap and compactmap, let’s do a quick recap of these higher-order functions and their purposes.

1. The `map(_:)` function applies a closure to an input collection, and returns the transformed collection
2. The `flatMap(_:)` function does the same, and it also flattens the resulting collection
3. The `compactMap(_:)` function does the same as `map(_:)`, and it also removes `nil` from the resulting collection

Working with `map(_:)`, `flatMap(_:)` and `compactMap(_:)` in the abstract makes it sometimes hard to imagine their practical use cases. Let’s discuss why you’d want to use them.

Using functions like map(_:) to apply transformations to sequences has a few advantages:

• It’s more concise than using a `for` loop, because you don’t need temporary variables and a multi-line `for in { }` block.
• You can typically write a call to `map(_:)` on one line, which (usually) makes your code more readable.
• Functions like `map(_:)` can be chained, so you can apply multiple transformations to a sequence one by one.

In general, higher-order functions are useful because they let you apply a function to a sequence of values. Instead of coding the transformation procedurally, you can just apply the function and get a result back.

The most practical use case for `flatMap(_:)` is working with input values that are grouped, or nested, but the output value you want needs to be one-dimensional.

You could, in a music app for example, have 3 arrays: songs, artists and playlists. You combine them in one array, call `flatMap(_:)` on it, select the songs, artists and playlists for which `isFavorite` is `true`, and end up with one flat list of items that have been favorited.

A practical use case for `compactMap(_:)` is working with a transformation that can return `nil`. You save yourself a few trivial steps by letting `compactMap(_:)` filter out `nil` values immediately. An added benefit is `compactMap(_:)`‘s non-optional return type; the `filter(_:)` function, in comparison, would have returned an optional value if you filtered out `nil`.

You can combine `flatMap(_:)` and `compactMap(_:)`, and even `filter(_:)` or `reduce(_:_:)`. Imagine you’re building a social media app. You want to construct a timeline of posts for a user. You use 3 queries to select post IDs for the user, for instance from follower posts, advertisements, and trending topics.

• You can use `map(_:)` to expand these IDs into actual `Post` objects
• You can use `flatMap(_:)` to flatten the 3 groups into one collection
• You can use `compactMap(_:)` to discard posts that couldn’t be expanded

Neat!

It’s worthwhile to learn about `map(_:)`, `flatMap(_:)` and `compactMap(_:)`, because they make your code more concise and readable. You can add more functional programming to your app’s code. Once you get used to them, you can’t believe you could do your work without them.

Especially the differences between `map(_:)`, `flatMap(_:)` and `compactMap(_:)` are worth pointing out. The first one applies a transformation to a sequence, the second one flattens the resulting array, and the third one removes `nil` values before returning its result. Awesome!