Higher Order Functions in Swift – map, compactMap, flatMap

Higher Order Functions in Swift – map, compactMap, flatMap
⏱ Reading Time: 6 mins

In the previous post I made an introduction to higher order functions in Swift, explaining what such a function is and how to create your own. In this post I am going to focus on some specific higher order functions bundled in Swift, and I will continue doing so on the next post as well.

Today I will focus on three higher order functions. One could tell that they belong to the same family; however they have certain differences and each one comes to serve a specific purpose and use case. These are the map, compactMap, and flatMap higher order functions.

It’s really important and necessary to underline that all map functions work with collections; arrays, dictionaries, and sets. It’s common, however, to see them being used more often with arrays, as that’s the kind of the collection mostly used when coding.

The map higher order function

The purpose of the map higher order function is to go through all elements of a collection, operate on every single element, and return a new collection with the results. In other words, it gets a collection as input, it does something with it, and returns another collection.

In addition, map function, as any other higher order function, helps us write shorter and cleaner code. That happens because with a single line of code we can replace a bunch of lines with custom implementation that would be performing the exact same operation on a collection.

The following examples will help clear all that out. To see everything in action, just open a playground in Xcode and use the code that you will be meeting along the way.

Let’s begin with the following array; it contains the names of ten random countries:

For the sake of the example, let’s suppose that we want to come up with a new array, where each item will be showing the length of each country name. Let’s do so with a custom implementation first, without using any higher order function:

The first step is to initialize a new array (called namesLength above). Then, inside a for-in loop we get the length of each name string, and then we append it to the new array.

Let’s use now the map higher order function. To do so, we access it with the dot syntax in the original collection. It accepts a method or a closure as argument, where inside its body the custom logic is being implemented. Here we’ll be returning the length of each country name. The most common approach is to use a closure, and a closure’s body is automatically appended by following Xcode’s auto-suggestion:

The parameter value in the closure represents each country name in the countries array. There is also a return value; in this example we want to return an integer number that shows the length of each country name.

The above implementation is definitely shorter than the custom one, however it can become even shorter by omitting the return type and the return keyword inside the closure’s body:

Even though the return type is not explicitly specified anymore, it’s automatically inferred based on the return value resulting from any calculations or operations in the closure’s body.

The above can become even shorter and simpler, and this time we will omit the parameter value. Instead, we will use a shorthand argument:

And of course, we can write the above in one line only:

Not only we replaced the original five lines of code with a single line, but the above statement is so much cleaner comparing to the initial implementation. map function creates a new array, appending to each position the length of the respective country name, while iterating through all names in the countries array behind the scenes.

All the above code snippets are equivalent and they return the following array:

Here is one more example using the map function. It converts all country names to uppercase, appending them to a new array:

The printed array is the following:

map higher order function is also quite useful when working with dictionaries. One of its greatest advantages is that it makes it really easy to separate keys and values, and therefore get two different arrays containing the one or the other.

Let’s take as example the following dictionary, which contains the countries from the previous examples and the continent they belong to:

The following two lines return both keys and values in two different arrays:

But map‘s usefulness does not stop in the above; we can easily get a new dictionary after having modified either keys or values and with an additional step with map.

To showcase that, suppose that we want to come up with a new dictionary that contains all values (continents in this example) uppercased. Obviously the following would not be correct, because it returns an array with all values only:

The trick that will actually work is the following:

See that a tuple is returned from the map‘s closure. The first value is each key, and the second is each value uppercased. results array is an array of tuples, and that has a purpose. We can use it by providing it as argument to a specific initializer of the Dictionary type, and therefore end up with a new dictionary:

Printing the updatedCountriesAndContinents will show this:

The compactMap higher order function

If you know how map works, then in a similar way you can use both compactMap and the flatMap function that you’ll read about next.

compactMap works exactly as the map does, with one big difference though; it ignores any nil values in case they exist in the original collection.

Let’s take the countries array as example once again, only this time nil values have replaced some actual country names:

To keep things simple, let’s assume that once again we want to get country names uppercased. Using map, we would need to optionally unwrap the map‘s argument:

The resulting array will be containing the following optional and nil values together:

This may suit you sometimes, but if you want to get results for those countries that have an actual name in the original array, then compactMap is what you really need to use:

The resulting array contains no optionals this time, and nil values have been omitted:

Knowing what compactMap does now, use that instead of map when you want to get rid of nil values, or just neglect them in the original collection. Besides that, everything else discussed about the map function applies here too.

The flatMap higher order function

This higher order function is handy when you need to get a single collection out of other collections contained in each other. To clarify that, imagine that the following array keeps the average temperature of a city in the morning and in the afternoon (in Celsius degrees):

Now suppose that we have seven arrays like the above into another array, matching to the seven days of the week:

Now, what if we wanted to calculate the average temperature of the whole week? It would be really convenient if we could have all temperatures existed in a single array.

This is what flatMap does; it merges values from nested collections into one, single collection. All it takes to achieve that is the following:

All temperatures exist now in a single array:

The above transformation makes it much easier to process these values and do something with them.

Summary

After the initial introduction to higher order functions in the previous post, here I briefly described how to use some common and similar higher order functions provided in Swift, explaining what they are for, how they work, and how they can help to write better code. More of them are coming to the next post, so don’t miss it. Thank you for your reading time!

The next post about forEach, filter and sorted higher order functions is available here.

If you found this post useful then please consider sharing it! Also, subscribe to my newsletter in order to be notified about everything new published here directly in your inbox, and follow me on Twitter, on YouTube, on Medium and other social media.