Hi there Swifters.
Today, I’m going to show you something I learned only quite recently about Swift. In Swift you can use the map function on an optional.
At this point I must insert a simple check, if you say:
- ‘Wow, you found out about this only now, I’ve known about it for a long time’ or
- ‘Big deal, I don’t care, I don’t use these fancy new tricks everyone seems to promote lately’ or
- ‘I actually wrote the method’ or
- something similar
then you can simply stop reading this post.
If you’ve heard about the map function, you probably have seen something like this as an explanation. This is using of the map function on an array.
let array = [1, 2, 3] let mappedArray = array.map { $0 * $0 } print(mappedArray)In this specific example we declare an array: array containing the integers 1, 2 and 3. Then we declare another array: mappedArray which is derived from array by specifying that each element in mappedArray should be calculated by multiplying the element at the same position in the array array by itself. A more in-depth explanation on how to use the map function on an array is out of this post’s scope so if you want to learn more about it I am recommending you checkout Apple’s official documentation about it.
For me, this usage of map was the only one I knew till I watched the videos of the presentations Using Monads and Other Functional Paradigms in Practice by Raheel Ahmad and Map and FlatMap Magic by Neem Serra. I highly recommend watching them both since they explain how you can use the map function on optionals but also many many more cool stuff.
Back on the topic…Let’s analyze a simple use case that comes up pretty often when I write my code and I’ve also seen it a lot in other Swift code from the world. Let’s assume we have a value of an optional integer type. We want to somehow execute an algorithm which will take that value of an optional integer type, do something with it and return a value of an optional integer type. That ‘do something’ part should be: if the optional integer is not nil (has some value) then return that value multiplied by itself; if the optional integer is nil then return nil as well.
Please, take a look at the following code section (nothing scary here)…
func transformWithIfLet(number: Int?) -> Int? { if let number = number { return number * number } return nil }<span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0" ></span> func transformWithMap(number: Int?) -> Int? { return number.map { $0 * $0 } } func executeTests() -> Bool { let inputNumbers: [Int?] = [nil, 3] for inputNumber in inputNumbers { if transformWithIfLet(number: inputNumber) != transformWithMap(number: inputNumber) { return false } } return true } print(executeTests() ? "Tests passed" : "Tests failed")The transformWithIfLet(number: Int?) -> Int? function is a modularized version of the algorithm implemented in a way that the code maps the worded explanation mostly word for word. The transformWithMap(number: Int?) -> Int? implements the same requirement by using the map function on the number value of optional integer type. When the executeTests() function is executed, the ‘Tests passed’ string is printed out assuring us that these two implementation of the same algorithm return the same values for the same inputs.
That’s basically how you use the map function on an optional. You define a closure which takes one argument of the same type only not optional (e.g. if you use it on Int?, the argument is of type Int). The closure should return a value of the same type as the closure input argument’s type. That closure will be executed only if the value of the optional is not nil, otherwise nil is returned immediately. Neat, simple and cute syntax!
Still, you might say ‘Great, I traded 5 lines for 1 at the cost of having to use a bit of slightly unorthodox syntax. I don’t see I get much benefit here’. This is the point where we need to go deeper to a more complex use case. If we modify the algorithm to add two more steps in the transformation after the multiplication when the optional value is not nil: converting the integer to a float and then converting it to a string, this is the code we’d get for the two versions of the implementation:
func transformWithIfLet(number: Int?) -> String? { if let number = number { return String(Float(number * number)) } return nil } func transformWithMap(number: Int?) -> String? { return number.map { $0 * $0 }.map(Float.init).map(String.init) }In my opinion the implementation of transformWithMap(number: Int?) -> String? is a lot more intuitively understandable than the implementation of transformWithIfLet(number: Int?) -> String?. The actual chain of transformation is more evident and the code representation matches the mental representation a lot more closely.
So, there you go, you just leaned how to use a short and quick trick from the functional programming repertoire, at your disposal 24/7. If you learned something new from this post, please spread the message to your programming buddies by sharing it. Happy optional mapping!