Swift — Map() Vs compactMap() Vs flatMap()
We often get confused with the Higher Order Functions (HOF) of map types, So here I explain the difference between these three map functions:-
Map()
This takes a value out of a container, applies a function to it, then puts the result of that function back into a new container that gets returned to you.
Swift’s arrays, dictionaries, and sets have map() built-in, and it’s commonly used to loop through every item in an array while applying a function to each value.
This snippet converts strings to uppercase:
compactMap()
Swift has a specialized form of map() called compactMap(), which applies a compacting step once the map() operation has been completed: all optionals get unwrapped, and any that contained nil get discarded.
This snippet is a use-case for compactMap():
let numbers = ["1", "2", "3", "4", "5", "notANumber"]
let mappedNumbers = numbers.map { Int($0) }
let compactMappedNumbers = numbers.compactMap { Int($0) }
print(mappedNumbers) // [1, 2, 3, 4, 5, nil]
print(compactMappedNumbers) // [1, 2, 3, 4, 5]
flatMap()
The flatMap() method is another variation of map(), and it also has variants for both sequences and optionals. Both variants have the same goal, and the clue is in the name it’s a flattening map operation.
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, each containing 3 numbers.
The flatMap() method on sequences is effectively the combination of using map() and joined() in a single call, in that order. It maps items in array A into array B using a function you provide, then joins the results using concatenation. flatMap() is result of below:
Array(someArray.map(transformFunction).joined())
Optional flatMap()
Here’s a practical example so you can see the difference:
When that code runs, intNumber will be an Int?? — an optional integer. This is because we already have optionality from stringNumber, and the Int initializer from a string also returns an optional, so map() just puts them together.
In comparison, flatMap() acts differently. That will return a regular Int?, meaning that either the whole thing exists or nothing exists — it’s easier to work with.
If you’re not sure whether to use map() or flatMap() with your optional value, the simple rule is this: if your transformation closure returns an optional you should use flatMap(), to avoid optional optionals.
Why Use FlatMap and CompactMap?
A quick recap of these higher-order functions and their purposes.
- The map(_:) function applies a closure to an input collection, and returns the transformed collection
- The compactMap(_:) function does the same as map(_:), and it also removes nil from the resulting collection.
- The flatMap(_:) function does the same, and it also flattens 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.
Happy reading :-)