Higher order functions in swift: Filter, Map, Reduce, flatmap

Ever since I learned about functions and closures in detail, I wanted to know more about the advantages and usages of these in programming. I read about higher order functions that can be used on collection types and this is what I understood.

I don’t know who that is over there at the top.
As far as I understood, higher order functions are functions that takes another function/closure as argument and returns it.

I will try to explain this first. Consider the following code which will give you an idea what a higher order function is :

Pass function to another function:

higher order functions in swift

The first two methods are of type (Double,Double)->Double . First one accepts two double values and return their sum . The second one returns the product of these two double values. The third method is a higher order function which accepts three parameters. Two double values and a function of type (Double,Double)->Double . Do have a look at the method call. You will understand how a higher order function works.

Return function from another function:

Here , the function doArithmeticOperation(isMultiply:) is a higher order function which returns a function of type (Double,Double)->Double .

Functions and closures are first class members in swift. They can be saved into a variable and passed around.

So, here , based on the bool value passed into the doArithmeticOperation(isMultiply:) function, it returns the function which does the operation.

operationToPerform_1 is a function that does the multiplication for you.

operationToPerform_2 is a function that does the addition for you.

Just have a look at the function definition and call. You will understand everything.


Of course, you can do the same stuff in plenty of different ways. May be you could use a closure instead of functions. You could create an enum of arithmetic operations and simplify the function. I just meant to explain what s higher order function is .


Here are some of the higher order function used in swift. If I understood correctly, the following functions uses closures as argument . You can use these functions to operate on Swift collection types such as Array, setor Dictionary .

You should understand what a closure is before going through the following. Read my article on closures

Map

Use map to loop over a collection and apply the same operation to each element in the collection. The map function returns an array containing the results of applying a mapping or transform function to each item.

Map on array:

Suppose we have an array of integers:

let arrayOfInt = [2,3,4,5,4,7,2]

What if we have to multiply every number by 10 ? We normally use a for-in loop to iterate through every number and do the operation right?

var newArr: [Int] = []
for value in arrayOfInt { newArr.append(value*10) }
print(newArr) // prints [20, 30, 40, 50, 40, 70, 20]

This code looks verbose. There are some boilerplate code like creating a new array, which could be avoided using map. The swift autocomplete shows the following if we try to map the Int array.

maps on int array

The closure transform accepts an int argument and returns a generic type.

let newArrUsingMap = arrayOfInt.map { $0 * 10 }

This is the shortest version of using map on an array of Int. I used the shorthand syntax of closures using $ operator.

All the following code will work the same as above , but a complex to simplified syntax. You should have a good knowledge in closures to do that.

simplifying the map closure syntax

Working of map: The map function has a single argument which is a closure (a function) that it calls as it loops over the collection. This closure takes the element from the collection as an argument and returns a result. The map function returns these results in an array.

Map on Dictionary:

Consider a dictionary with book names as key and the amount of each book as the value.

let bookAmount = [“harrypotter”:100.0, “junglebook”:100.0]

If you try to do a map function on a dictionary, the swift autocomplete will look like this:

map on dictionary

Here, for the above dictionary, as we iterate over the collection our closure has arguments that are a String and a Double from the types of the key and value that make up each element of the dictionary. The return type can be an array of uppercased keys, values array with discounts or even an array of tuples. It’s all upto you.

map on dictionary return values
Note: The return type of a map function is always a generic array. You can return an array of any type.

Map on set:

In this case we have a set containing elements of type Double so our closure also expects a Double. lengthInMeters is a set. lengthInFeet is an array.

What if you want to know the index of the collection while applying map to it??

Answer is simple. You will have to enumerate it before mapping.

Check the code below.

let numbers = [1, 2, 4, 5]
let indexAndNum = numbers.enumerated().map { (index,element) in
return "\(index):\(element)"
}
print(indexAndNum) // [“0:1”, “1:2”, “2:4”, “3:5”]

Filter

Use filter to loop over a collection and return an Array containing only those elements that match an include condition.

Filter on array

Consider the following code to filter even numbers from an array of integers.

filter using for-in loop

Now, like map , there is a simple method to do the filtering stuff for collection types.

The swift autocomplete shows the following if we try to use filter method for an Int array.

As you can see, the filter function calls a closure called isIncluded which takes one int as argument and returns a Bool . So, the isIncluded closure will return a bool value for each collection item and based on this result a new filtered array will be generated.

The filter method has a single argument that specifies the include condition. This is a closure that takes as an argument the element from the collection and must return a Bool indicating if the item should be included in the result.
filter an int array

The filter closure can be further simplified like we did for map.

Simplified filter closure on Int array

Filter on dictionary

Consider a dictionary with book names as key and the amount of each book as the value.

let bookAmount = [“harrypotter”:100.0, “junglebook”:1000.0]

If you try to do a filter function on a dictionary, the swift autocomplete will look like this:

filter autocomplete on dictionary

The filter functions will call a closure called isIncluded by passing each key-value pair and do the condition check (here, it accepts a String and Double as arguments). Finally, based on the bool value returned, the filter function will decide whether or not to add the key-value pair in the returned array.

Important: Filter function on dictionary returns an array of Tuples as you can see in the playground code below:

This can be further simplified as:

let filteredArrayOnDict = bookAmount.filter { $1 > 100}
$0 is the key, $1 is the value

Filter on set

filter on set

Here, the filter closure for set takes a Double argument and returns a Bool for each element in the set. Based on this Bool value, the item is included in the filtered array.

IMPORTANT: The return type is a filtered array.


Reduce

Use reduce to combine all items in a collection to create a single new value.

Declaration as in apple docs:

func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

So, the reduce function takes two arguments.

  • One is an initial value which is used to store the initial value or the value or result returned by the closure from each iteration.
  • The other one is a closure which takes two arguments, one is the initial value or the result from the previous execution of the closure and the other one is the next item in the collection.

Reduce on arrays

Let’s understand this with an example:

Check the following array of numbers.

reduce method on arrays

This reduce function will iterate four times.

  1. Initial value is 0, x is 0, y is 1 → returns x+y . So, initial value or Result becomes 1.
  2. Initial value or Result is 1, x is 1, y is 2 → returns x+y . So, initial value or Result becomes 3 .
  3. Initial value or Result is 3, x is 3, y is 3→ returns x+y . So, initial value or Result becomes 6.
  4. Initial value or Result is 6, x is 6, y is 4→ returns x+y . So, initial value or Result becomes 10

The reduce function can also be simplified like:

let reducedNumberSum = numbers.reduce(0) { $0 + $1 }
print(reducedNumberSum) // prints 10
reducing on int array
Here the closure is of type (Int,Int)->Int . So, we can pass any function or closure of type (Int,Int)->Int . In this scenario, instead of the closure, we could also pass basic operator functions like +, -, *, / .
let reducedNumberSum = numbers.reduce(0,+) // returns 10

We can also use multiplication or other operation or logic inside the closure.

let reducedNumberSum = numbers.reduce(0) { $0 * $1 }
// reducedNumberSum is 0...

the above line can also be written as:

let reducedNumberSum = numbers.reduce(0,*)

Reduce will also work with strings using the + operator to concatenate:

let codes = ["abc","def","ghi"]
let text = codes.reduce("") { $0 + $1} //the result is "abcdefghi"
or
let text = codes.reduce("",+) //the result is "abcdefghi"

Reduce on dictionary

Let’s reduce the bookAmount Dictionary:

let bookAmount = [“harrypotter”:100.0, “junglebook”:1000.0]
reducing dictionary

For dictionary, the closure inside the reduce function takes two arguments.

  1. An initial or result value of the type that should be reduced to.
  2. A tuple of current key-value pair.

The reducedBookNamesOnDict2 can be further simplified using the short closure syntax like:

let reducedBookNamesOnDict = bookAmount.reduce(“Books are “) { $0 + $1.key + “” } //or $0 + $1.0 + “”

Reduce on set

The reduce on set works the same way as in arrays.

Return type is the return type of closure. Here it is Double.

Flatmap

Flatmap is used to flatten a collection of collections . But before flattening the collection, we can apply map to each elements.

Read it like : map + (Flat the collection)
(fig — 1) Read the above definition and have a look at this code.
(fig — 2)… and this (string is a collection from swift 4).

In the (fig — 2) above, the flatMap iterates through all the collections in the collection called codes. Here, the individual collections are string (string is a collection from swift 4). Here are the steps:

1 . Apply upperCased() function to all strings. This is similar to

[“abc”,”def”,”ghi”].map { $0.uppercased() }

Output will be:

output: [“ABC”, “DEF”, “GHI”]

2 . Flatten all the sub — collections to one single collection.

output: ["A", "B", "C", "D", "E", "F", "G", "H", "I"]

Finally, this is what flatMap does to a string

Tip:

If you do flatmap a collection containing optional values, flatmap will only consider the non-nil values. Have a look at the code below:

The output of map became a collection of optional int ([Int?]) only because the array had nil — value in it. Otherwise it would have been an Int array.

I think, now it is clear what a flatMap does.

Flatmap on array

flatmap of array of array of int

Flatmap on array of dictionaries

It returns an array of tuples after flatmapping. We have to convert it to an array:

flatmap an array of dictionaries

Flatmap on set

flatmap on set

Advantages of flatmap:

Let’s dig into the advantages of flatmap:

Removing optionals:

Even more usefully it knows about optionals and will remove them from a collection.
flatmap to remove optionals.

Flatmap by filtering or mapping

We can apply flatmap on collection of collections. ie; an array of arrays will be flattened to a single array. So, the flatmap closure takes a single collection of argument, do something with this collection and /or return the collection. Here , before returning the collection, we can apply filter or map or even reduce.

filtering while flatmap

When applying the short closure syntax:

let onlyEven = collections.flatMap { $0.filter { $0 % 2 == 0 } }

Chaining : (map + filter + reduce)

We can chain multiple higher order functions one by one.

You can read my article on chaining methods to understand its working.
The working principle behind the chaining is simple. The map and filter methods act on a collection and returns another collection . And now, we can again apply another higher order function on this collection. It’s that simple.

Let’s say we want to add the squares of all the even numbers from an array of arrays.

add the squares of all the even numbers from an array of arrays.

Conclusion

Well, thats it!! You mastered Higher order functions!!

Use Higher order functions where ever possible:

  • It improves your swift skills.
  • Enhances readability of code.
  • More functional programming.
Our objective is to write less code which makes more sense!!!

Enjoy!!

Reference: Link1

If you enjoyed reading this post, please share and give some clapps so others can find it 👏👏👏👏👏 !!!!

You can follow me on Medium for fresh articles. Also, connect with me on LinkedIn and Twitter.

If you have any comment, question, or recommendation, feel free to post them in the comment section below!