# When to use map, flatMap and compactMap in Swift

Published on: October 23, 2019

Any time you deal with a list of data, and you want to transform the elements in this list to a different type of element, there are several ways for you to achieve your goal. In this week’s Quick Tip, I will show you three popular transformation methods with similar names but vastly different applications and results.

By the end of this post, you will understand exactly what `map`, `flatMap` and `compactMap` are and what they do. You will also be able to decide which flavor of map to use depending on your goals. Let’s dive right in by exploring the most straightforward of the three; `map`.

## Understanding map

Any time you have a set of data where you want to transform every element of the sequence into a different element, regardless of nested lists or `nil` values, you’re thinking of using `map`. The working of `map` is probably best explained using an example:

``````let numbers = [1, 2, 3, 4, 5, 6]

let mapped = numbers.map { number in
return "Number \(number)"
}

// ["Number 1", "Number 2", "Number 3", "Number 4", "Number 5", "Number 6"]
print(mapped)``````

The example shows an array of numbers from one through six. By calling `map` on this array, a loop is started where you can transform the number and return another number or, like in this example, something completely different. By using `map`, the original array of numbers is unaltered and a new array is created that contains all of the transformed elements. When you call map on an array with nested arrays, the input for the `map` closure will an array. Let’s look at another example:

``````let original = [["Hello", "world"], ["This", "is", "a", "nested", "array"]]
let joined = original.map { item in
return item.joined(separator: " ")
}

// ["Hello world", "This is a nested array"]
print(joined)``````

By joining all strings in the nested array together, we get a new array with two strings instead of two nested arrays because each array was mapped to a string. But what if we wanted to do something else with these strings like for instance remove the nested arrays and get the following output:

``["Hello", "world", "This", "is", "a", "nested", "array"]``

This is what `flatMap` is good at.

## Understanding flatMap

The slightly more complicated sibling of `map` is called `flatMap`. You use this flavor of `map` when you have a sequence of sequences, for instance, an array of arrays, that you want to "flatten". An example of this is removing nested arrays so you end up with one big array. Let’s see how this is done exactly:

``````let original = [["Hello", "world"], ["This", "is", "a", "nested", "array"]]
let flatMapped = original.flatMap { item in
return item
}

// ["Hello", "world", "This", "is", "a", "nested", "array"]
print(flatMapped)``````

Even though the type of `item` is `[String]`, and we return it without changing it, we still get a flat array of strings without any nested arrays. `flatMap` works by first looping over your elements like a regular `map` does. After doing this, it flattens the result by removing one layer of nested sequences. Let’s have a look at an interesting misuse of `flatMap`:

``````let original = [["Hello", "world"], ["This", "is", "a", "nested", "array"]]
let flatMapped2 = original.flatMap { item in
item.joined(separator: " ")
}

print(flatMapped2)``````

This example joins the `item` (which is of type `[String]`) together into a single string, just like you did in the `map` example. I want you to think about the output of this `flatMap` operation for a moment. What do you expect to be printed?

I’ll give you a moment here.

If you thought that the result would be the same as you saw before with `map`, you expected the following result:

``["Hello world", "This is a nested array"]``

This makes sense, you’re returning a `String` from the `flatMap` closure. However, the actual result is the following:

``["H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "T", "h", "i", "s", " ", "i", "s", " ", "a", " ", "n", "e", "s", "t", "e", "d", " ", "a", "r", "r", "a", "y"]``

Wait, what? Every letter is now an individual element in the array? That’s correct. Since `String` is a sequence of `Character`, `flatMap` will flatten the `String`, pulling it apart into its individual `Character` objects. If this is not the result you had in mind, that’s okay. `flatMap` can be a confusing beast. It’s important to keep in mind that it will flatten anything that is a sequence of sequences into a single sequence. Even if it’s a `String`.

Let’s see how the last flavor of `map`, `compactMap` is different from `map` and `flatMap`.

## Understanding compactMap

The last flavor of `map` we’ll look at in this Quick Tip is `compactMap`. Whenever you have an array of optional elements, for instance `[String?]`, there are cases when you want to filter out all the `nil` items so you end up with an array of `[String]` instead. It might be tempting to do this using a `filter`:

``````let arrayWithOptionals = ["Hello", nil, "World"]
let arrayWithoutOptionals = arrayWithOptionals.filter { item in
return item != nil
}

// [Optional("Hello"), Optional("World")]
print(arrayWithoutOptionals)``````

This kind of works but the type of `arrayWithoutOptionals` is still `[String?]`. How about using a for loop:

``````let arrayWithOptionals = ["Hello", nil, "World"]
var arrayWithoutOptionals = [String]()
for item in arrayWithOptionals {
if let string = item {
arrayWithoutOptionals.append(string)
}
}

// ["Hello", "World"]
print(arrayWithoutOptionals)``````

Effective, but not very pretty. Luckily, we have `compactMap` which is used for exactly this one purpose. Turning a sequence that contains optionals into a sequence that does not contain optionals:

``````let arrayWithOptionals = ["Hello", nil, "World"]
var arrayWithoutOptionals = arrayWithOptionals.compactMap { item in
return item
}

// ["Hello", "World"]
print(arrayWithoutOptionals)``````

Neat, right? A somewhat more interesting example might be to take an array of strings and converting them into an array of `URL` objects. Immediately filtering out the `nil` results that occur when you convert an invalid `String` to `URL`:

``````let urlStrings = ["", "https://blog.donnywals.com", "https://avanderlee.com"]
let urls = urlStrings.compactMap { string in
return URL(string: string)
}

// [https://blog.donnywals.com, https://avanderlee.com]
print(urls)``````

We now have converted the input strings to valid `URL` instances, omitting any `nil` values. Pretty neat, right.

## In summary

This Quick Tip showed you three ways to convert an array or sequence of elements into a different kind of sequence. `map` is the most basic variant, it converts every element in the sequence into a different sequence. No exceptions. Next, we looked at `flatMap` which looks similar to `map` but it flattens the first level of nested sequences. You also saw how it would flatten an array of two strings into an array of many single characters because a `String` is a sequence of `Character`. Lastly, you learned about `compactMap`. This mapping function transforms all elements in a sequence into a different element, just like `map`, but it removes `nil` values afterward.

Now that you have learned about `map` on sequence types like arrays, you should have a vague idea of what might happen if you `map` a `Publisher` in `Combine`, or maybe if you call `map` on an `Optional`. If you’re not sure, I challenge to go ahead and try it out. You might find some interesting results!

Thanks for reading this post, and as always your feedback is more than welcome! Don’t hesitate to shoot me a Tweet if you have any questions, feedback or just want to reach out.