# What’s the difference between map(), flatMap() and compactMap()?

Three common functional methods explained.

Paul Hudson       @twostraws

Swift gives us `map()`, `compactMap()` and `flatMap()` methods, but although they might sound similar they do very different things. So, in this article we’ll look at `map()` vs `compactMap()` vs `flatMap()` to help you understand what each one does and when it’s useful.

The word all three methods share is “map”, which in this context means “transform from one thing to another.” So, the `map()` method lets us write code to double all the numbers in an array:

``````let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { \$0 * 2 }``````

That will take each value in the array and run it through our closure, where `\$0` refers to the number in question. So, it will be 1 2, 2 2, 3 * 2, and so on – `map()` will take a value out of its container, transform it using the code you specify, then put it back in its container. In this case, that means taking a number out of an array, doubling it, and putting it back in a new array.

It works on any data type, so we could use it to uppercase an array of strings:

``````let wizards = ["Harry", "Hermione", "Ron"]
let uppercased = wizards.map { \$0.uppercased() }``````

`map()` is able to return a different type from the one that was originally used. So, this will convert our integer array to a string array:

``````let numbers = [1, 2, 3, 4, 5]
let strings = numbers.map { String(\$0) }``````

Things get a little trickier if we go in the opposite direction – if we try to convert those strings back into integers. This is because strings can contain any value: “1”, “5”, and “500” are all strings that can safely be converted to integers, but “Fish” is not. As a result, converting a string to an integer returns an optional integer.

To see this in action, this code uses `map()` to convert a string array into an array of optional integers:

``let maybeNumbers = strings.map { Int(\$0) }``

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Sponsor Hacking with Swift and reach the world's largest Swift community!

### compactMap(): transform then unwrap

Working with optionals can be annoying, but `compactMap()` can make life much easier: it performs a transformation (the “map” part of its name), but then unwraps all the optionals and discards any that are `nil`.

So, this line of code does the same string to integer conversion, but results in an array of integers rather than an array of optional integers:

``let definitelyNumbers = strings.compactMap { Int(\$0) }``

There are lots of places in Swift that return optionals, including `try?`, `as?`, and any failable initializer like creating an integer from a string – these are all great candidates for `compactMap()`.

For example, if you have a `UIView` and want to read out all subviews that are image views, you can write this:

``let imageViews = view.subviews.compactMap { \$0 as? UIImageView }``

Or if you have an array of strings and want to know which ones are valid URLs, you can write this:

``let urls = urlStrings.compactMap { URL(string: \$0) }``

So, again: `map()` will take a value out of its container, transform it using the code you specify, then put it back in its container. `compactMap()` does the same thing, but if your transformation returns an optional it will be unwrapped and have any `nil` values discarded.

### Optional map(): transform only if we have a value

If you think about it, optionals are similar to arrays: they are also a container with something inside. When we look inside the optional container (when we unwrap the optional), we either find a value or we find `nil`.

This means that the `map()` method also exists on optionals: take the value out of its container (an optional), transform it with a closure we provide, then put it back in the container (another optional). If the optional was empty, `map()` automatically does nothing – it sends back `nil`.

To illustrate this, imagine we had a `getUser()` method that accepts an integer and returns the username with that ID if it exists. If it doesn’t exist it sends back `nil`, so this method will return an optional string.

We can use `map()` to read the value that was sent back, and transform it:

``````let name: String? = getUser(id: 97)
let greeting = name.map { “Hi, \(\$0)” }
print(greeting ?? “Unknown user”)``````

So, if `name` contains a string then `map()` will take it out of the optional, transform it to be “Hi, ” then the name, put it back into an optional, then send it back to be stored in `greeting`.

Putting the value back into an optional allows us to keep the “maybe it has a value, maybe it doesn’t” situation going longer so that later code can decide what that means. In this case the `print()` function will either print a greeting or print “Unknown user” – it gets to decide, rather than us forcing “Unknown user” earlier.

### flatMap(): transform then flatten

You’ve now seen `map()` transforming an array of integers into an array of integers (doubling them), transforming an array of integers into an array of strings, and transforming an array of strings into an array of integers. That last transformation returned optional integers, so we also looked at how `compactMap()` will perform the same transformation but then unwrap the optionals and discard any `nil` values.

Then we looked at how `map()` works on optionals: if it has a value it gets unwrapped, transformed, and rewrapped, but if it is `nil` then it stays as `nil`.

Now consider this code:

``````let number: String? = getUser(id: 97)
let result = number.map { Int(\$0) }``````

Walk it through step by step – what would `result` be after that has run?

• `number` is an optional string.
• `map()` will take the value out of the optional and transform it.
• In this case, `Int(\$0)` will convert the string to an optional integer because the string might be something non-numeric like “Fish”.
• `map()` then puts that optional value back into another optional.

So, when that code runs `result` won’t be an `Int` or even an `Int?` – it will be an `Int??`, which is an optional optional integer. Broadly speaking, any time you see an optional optional anything something has gone wrong and you should rethink.

To be clear, an optional optional means:

1. The outer optional might exist and the inner optional might exist.
2. The outer optional might exist but the inner optional might be `nil`.
3. The outer optional might be nil, which means there is no inner optional.

Optional optionals are deeply confusing to work with, but this is where `flatMap()` comes in: it also performs a transformation (the “map” part of its name), but then flattens what comes back so that “optional optional” just becomes “optional”.

That is, either the whole thing exists or nothing exists – it flattens double optionals down to single optionals. Ultimately we don’t care about whether the outer or inner optional exists, only whether there’s a value inside there or not, which is why `flatMap()` is so useful.

As a result, this code will set `result` to be `Int?` rather than `Int??`:

``````let number: String? = getUser(id: 97)
let result = number.flatMap { Int(\$0) }``````

### Mapping and flat mapping: universal friends

It’s possible to use `map()` and `flatMap()` with many other things, not just arrays and optionals, which is why we have a general name for types that allow them: functors and monads.

These names sound awfully grand, but it only takes a few minutes to understand:

Those articles outline the basic rules of functors and monads, and there’s a very good chance you’ll say “those rules are obvious!” Yes, they are obvious, but that doesn’t make them any less important – we can add a method named `map()` to anything we want, but that doesn’t automatically make it a functor.

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Sponsor Hacking with Swift and reach the world's largest Swift community!