Functional magic with Swift
Last few days I have been experimenting with functional magic in Swift. Especially how map
and flatMap
s work. I was so amazed to know how hassle-free my life became after I understood and harnessed the power of functional magic in Swift. I strongly encourage you to do so as well. In this article I have compiled a list of commonly used expressions that can be used in an app.
-
Let's say you want to make an array of UIViews. How would you do it? Add a for loop, declare an empty array and keep adding an individual element to it? Nah!
map
can do it automatically for you.let numberOfViews = 5 var allViews = (1...numberOfViews).map({ _ in UIView() })
-
Now you have all the views and you want to color each of them with color red.
var coloredViews = allViews.map { (view) -> UIView in view.backgroundColor = .red return view }
-
Now say, you don't want to color all views with same color, but rather color them differently based on the index. For the sake of this example, we will color
odd
indexed views withred
color andeven
indexed views withgreen
color.var coloredViewsWithIndex = allViews.enumerated().map { (index, view) -> UIView in if index % 2 == 0 { view.backgroundColor = .green } else { view.backgroundColor = .red } return view }
-
We have an array of integers and want to apply certain transformations on them, say double every integer
// First off, we will add an extension on an integer to double the value extension Int { func double() -> Int { return self * 2 } } // Next, we will apply map function with array of integers to double every value let doubledValues = (1...5).map{ $0.double() } // filter is another magic swift offers. Say now that we have doubledValues array and we want only those values greater than 7. We can apply filter function to eliminate unwanted values let filtered = doubledValues.filter{ $0 > 7 }
-
Let's say we have dictionary. (It could hold anything, but for the sake of this example let's say keys are of type int and values are of type string)
let sampleDictionary: [Int: String] = [1: "first", 2: "second", 3: "third"] // We can convert this dictionary into array of just keys with following line let onlyKeys = sampleDictionary.map{ $0 } // array of values let onlyValues = sampleDictionary.map{ $1 }
-
We can also modify the individual value by applying
map
to it// Usually when we want to add constant to optional integer, swift raises a compiler error. Following is invalid let val: Int? = 0 let result = val + 2 // Errors out with error saying we need to unwrap val // We can simply avoid this by directly applying map to val let addedValue = val.map{ $0 + 2 } // Note that addedValue is an optional int since we are doing arithmatic with input value which is also an optional int
-
Using
flatMap
to flatten 2D array into 1D arraylet array2D = [[1], [2, 3], [3, 4, 6, 7]] let flattened = array2D.flatMap { $0 } // flattened = [1, 2, 3, 3, 4, 6, 7] // We can even go further to double every value in array2D array and retain only those values greater than 10 let flattenedMoreComplicated = array2D.flatMap{ $0.map{ $0 * 2 }.filter{ $0 > 10 } } // flattenedMoreComplicated = [2, 4, 6, 6, 8, 12, 14]
-
Using
flatMap
to flatten 2D array and removenil
slet array2DWithNil = [[1], [2, 3, nil], [3, 4, 6, nil]] let flatArray = array2DWithNil.flatMap{ $0 } // flatArray = [1, 2, 3, nil, 3, 4, 6, nil] let flatArrayWithNoNil = flatArray.flatMap{ $0 } // flatArrayWithNoNil = [1, 2, 3, 3, 4, 6]
-
Reduce to combine values from an array
// Sum of integers in an array let intsToReduce = [1, 2, 3, 4] let totalInts = intsToReduce.reduce(0, +) // totalInts = 10 // combine words in an array let stringsToReduce = ["this", "is", "it"] let totalStrings = stringsToReduce.reduce("", +) // totalStrings = "thisisit"
-
Convert array of values with arbitrary type to array of only ints with
flatMap
// We want to convert array of arbitrary value types to an array of ints let weirdValues = ["1", "d", "ddd", "2"] // We will iterate over array and apply Int(val) function to every value. If value if not transformable to int, Int(val) will return nil which will be ignored by flatMap during conversion let saneValues = weirdValues.flatMap{ Int($0) } // saneValue = [1, 2]
-
map
can also be used to little more fancy usageSay we have a number which we want to convert to description which would say for example, "Total marks are 100". However, we want to show "No marks available" it value of marks is nil. We can accomplish it with a single line as follows
let marks: Int? = 100 // If marks is not nil, the code below will print "Total marks are 100". However, if marks is nil - ternary operator will fail and print "No marks available" instead let marksDescription = marks.map{ "Total marks are \($0)" } ?? "No marks available" print(marksDescription)
If you are using these functions in your application in ways that can be of interest for rest of us, I would love to hear from you. You can contact me directly through an email or Twitter handle @jayeshkawli
References: