Step Up Your Functional Game — Map and FlatMap Tricks

Tyler Milner
Atlas
Published in
10 min readOct 25, 2017

Swift has been out for more than three years now, and as I’ve learned the language, I’ve tried to incorporate more functional programming techniques into my code. Coming from an extensive background in Objective-C, I’ll admit that I really didn’t understand map and flatMap when first learning Swift. Eventually, I was comfortable enough with the concepts to start using them to replace some of my for loops. Until recently, that’s all I really considered map and flatMap to be useful for. It wasn’t until I attended Neem Serra’s Map and FlatMap Magic talk at try! Swift NYC 2017 that my eyes were opened to a world of other great uses for map and flatMap. Much of the content in this post is based on Neem’s presentation, so make sure to check out her talk on Realm Academy when you get a chance!

Map

The most basic definition I could come up with for map is that it transforms data into a different type. Typically, it’s used to loop over a collection and transform each element so you can do things like create an array of Strings from an array of Ints.

Apple’s documentation for map on the Array type.

FlatMap

Like map, flatMap transforms data into a different type. FlatMap also has a special property of “removing a layer.” One example is removing a layer of nesting, where a 2-dimensional array is “flattened” into a 1-dimensional array. Another example is removing nil elements from an array that’s populated with optionals.

Apple’s documentation for flatMap on the Sequence type.

Map and FlatMap Basics

Setup

As in Neem’s talk, I’m going to showcase map and flatMap techniques using cupcakes. Here’s the model we’ll be working with:

Cupcake model — a simple struct.

As you can see, the model is dead simple. A Cupcake has a flavor property and there is one method to retrieve that flavor. In reality, there’s no need for the getFlavor() method since we could just access the flavor property directly. But for our example, we’ll leave it as is.

Up next, we’ll create some Cupcakes and stick them in a cupcakes array:

Creating an array of Cupcakes.

The rest of the examples will use this basic setup to illustrate different map and flatMap techniques. A playground is also provided at the end of the article.

Map — The Old Way

Does this code look familiar? I know I was guilty of writing code in this style when first learning Swift. Here, we’re transforming and array of Cupcakes into an array of Strings.

The old way — Transforming [Cupcake] into [String] using for.

Map — The New Way

Using map, we can reduce the 4 lines of code above into a single line. Neat!

The new way — Transforming [Cupcake] into [String] using map.

Map — The Wrong Way

You should really only use map to transform elements. While it’s technically possible to generate elements using map, you should avoid the urge to do so, if possible. A good rule of thumb is to watch for ignoring the closure parameter. If you’re writing something like .map { _ in ... } (note the underscore), then you’re probably not using map correctly.

The wrong way— Using map to generate elements rather than transform them.

Mapping with Optionals

Map can also be used on optionals. For reference, let’s look at how optionals normally behave when you try to use them without first unwrapping them. You’ll most definitely see a compiler error complaining about the optional not being unwrapped.

Compiler error when trying to use an optional without unwrapping it first.

You can use map to work around this, using it to unwrap the optional, if it exists, and give you a chance to work with the value.

The closure passed to map will only be executed if the value for the optional exists.

It’s important to note that the type of the newCount variable above remains an Int? or Optional<Int>. An easy way to think about it is when you pass an optional to map, map will unwrap it and let you do something with the value inside of the closure. Map will then re-wrap it as an optional and return it to you.

FlatMap — The Old Way

When dealing with nested, or multi-dimensional arrays, our code would often look something like this when we needed to reduce it down to a single-dimensional array.

Using a for loop to convert [[Cupcake]] into [Cupcake].

FlatMap — The New Way

Because of flatMap’s ability to “remove a layer,” we can perform the same operation in one line.

FlatMap converts [[Cupcake]] into [Cupcake].

FlatMapping with Optionals

Like map, flatMap can also be used with optionals. When you pass an optional in to flatMap, flatMap will unwrap the optional if it exists, giving you a chance to do something with the value, or return nil. A neat trick is using the nil-coalescing operator to then provide a default value in case the unwrapping fails.

Let’s say on some days there is a “featured cupcake,” which becomes the flavor of the day. Whenever there is no “featured cupcake,” we’ll say that vanilla is going to be the default flavor of the day. We can model this scenario in a single line using flatMap.

Using flatMap with the nil-coalescing operator allows you to unwrap an optional and provide a default value.

Note that the type of flavorOfTheDay is not an optional. Since we’re using the nil-coalescing operator to provide a default value, flavorOfTheDay is simply a String.

Map Tricks

Add Optional Subview — The Old Way

UIView’s addSubview(…) method takes a non-optional UIView so you would usually find yourself having to unwrap the optional view first.

The old way — using if let to unwrap the optional first.

Add Optional Subview — The New Way

Since map only runs its closure if the optional value exists, we can simplify the above code down to one line. No more if let for this simple operation!

The new way — using map to unwrap and add the subview.

String Interpolation — The Old Way

Let’s say we’re needing to display the number of cupcakes to the user. If the numberOfCupcakes variable is nil, we want to display the text “Unknown number.” Otherwise, we need to generate a String describing how many cupcakes are in the box. Without map, we may have done it like this.

Using var and if let to generate a String based on the value of an optional.

You may be familiar with the ternary operator. Maybe we could use it to simplify things.

Using the ternary operator to generate a String based on the value of an optional.

Unfortunately, the use of the ternary operator breaks our requirements. When numberOfCupcakes is not nil, there will be undesirable output because the full description of the optional will be used instead of its value (Optional(0) instead of 0).

Output from ternary operator use yields undesirable Optional(0) text.

The nil-coalescing operator can be used to fix. By adding ?? 0, it allows the value of numberOfCupcakes to be automatically unwrapped, falling back to the default value of 0 if the unwrapping fails. However, this is a bit ugly. Because the ternary operator will cause the code to short circuit to “Unknown number” when numberOfCupcakes is nil, the code path created with the nil-coalescing operator will never be executed.

Using the nil-coalescing operator to provide a default value to the optional unwrapping.

String Interpolation — The New Way

Using map, in combination with the nil-coalescing operator, we can adhere to our requirements while avoiding the creation of “unrunnable” code paths.

Using map and the nil-coalescing operator to generate a String based on the value of an optional.

Shortcut on Init — The Old Way

As we’ve seen before, we can use map to transform elements in an array into a different type. Using the shorthand notation, the implicit $0 parameter can be passed into the initializer of the desired type.

The old way — using map closure with implicit $0 parameter.

Shortcut on Init — The New Way

Since functions are first-class citizens in Swift, we can pass the init function directly to map as a parameter and avoid writing a closure at all.

The new way — passing an init function into map directly — no closure needed.

I’m still on the fence about adopting this technique throughout my code. On one hand, it’s kind of a nice simplification to how you call map. On the other hand, it can’t be used in every situation so it would create minor inconsistencies in your usage of map throughout your codebase.

FlatMap Tricks

Filter Objects That Fail on Init — The Old Way

Here’s another snippet of code that probably looks familiar if you come from an Objective-C background like myself. We have some identifiers that represent images that need to be loaded from the bundle into an array. Since UIImage’s image(named:) initializer is failable, we need to use an if let when creating the images.

The old way — using if let to unwrap the result of a failable initializer.

Filter Objects That Fail on Init — The New Way

Let’s look at what happens when we use map.

The new way — using map with a failable initializer.

Since map attempts to unwrap an optional and then rewrap it, we actually get back an array of optional images — [UIImage?]. Not quite what we want.

Result of using map with a failable initializer — an array of optionals.

Because flatMap removes a layer, it will return the value of the optional, if it exists, or nil. This means the images array is of type[UIImage]. Whenever the initializer fails, nothing will be added to the array instead of an Optional<UIImage> with a nil value.

The new way — using flatMap with a failable initializer.

In the attached playground, the array is empty because there are not actually any image resources included.

Result of using flatMap with a failable initializer — an empty array if the initializer failed for all elements.

Get Data for Onscreen Cell — The Old Way

Sometimes you need to get the data associated with a cell that’s shown in a table or collection view and doesn’t have a direct reference to the corresponding IndexPath. Typically, you would use if let to unwrap an IndexPath using UITableView’s indexPath(for:) method. Once you have the index of the cell, you typically use the row property as the index for your data array.

The old way — using if let to get the IndexPath for a table view’s cell.

Get Data for Onscreen Cell — The New Way

FlatMap’s property of transforming an optional, if it exists, or returning nil can provide an alternative implementation. If the indexPath(for:) method returns a valid IndexPath, we can use it to access the corresponding piece of data inside of the flatMap closure.

The new way — using flatMap to transform an optional IndexPath into the corresponding cell data.

Why Use Map and FlatMap?

Be Functional

Functional programming is cool, and we’ve only recently been able to take advantage of this tool thanks to the release of Swift.

One great aspect of functional programming is the ability to avoid mutable state. Not only can this help avoid potential bugs, but I think it also reads better, requiring less overall cognitive overhead.

When you see something is let, you know immediately that the value of that variable will not change for the rest of its scope. When it comes time to actually define the value of the variable, I find that transforming an optional using flatMap then using the nil-coalescing operator to define a default value reads very well. To me, a single line is easier and quicker to understand than having to track the value of a variable through several lines of an if/else block.

Visually Explicit Chaining

Visually Explicit Chaining refers to the ability to chain together several map and flatMap operations to define a stream of sequential changes that transform data over time.

Borrowing a slide from Neem’s presentation, the instructions are to combine boxes, remove nil cupcakes, frost the cupcakes, and then add sprinkles.

Using visually explicit chaining to apply transformations in a sequential, easy to follow manner — photo credit to Neem Serra.

The result is an array of sprinkled Cupcakes. If you’re reading closely, you may have also noticed that the print(cupcakes) statement should actually be print(deliciousness). 😉

Conclusion

A Swift playground is available as a GitHub Gist that contains all of the examples shown here. It was written in Swift 4 / Xcode 9.

Map and flatMap have many more use cases beyond the obvious one of transforming arrays into different types. I hope I’ve introduced you to at least one cool new trick that you may start adopting in your Swift code. Do you know of any other great uses for map and flatMap? If so, feel free to drop a comment and help us all step up our functional game!

--

--