Swift Higher Order Functions

Posted on Feb 7, 2021

Let’s examine Higher Order Functions in Swift Programming Language. Higher Order Functions are basically generic functions on Swift Standart Template Library to handle some processes on Collections with easier way to use.

Map

Map is the one of the best known higher-order function. Let’s start with the description of it.

func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

As you can see, it’s generic and there is an callback to transform Element to T type. T can be anything.

For example, in a scenario, you have an array of celcius degrees and you have to convert it them to Kelvin so basically you can write a for-loop like below.

# MARK: - formula: T(K) = T(°C) + 273

var celciusArray = [10,20,30,-10,45]
var kelvinArray: [Int] = [Int]()

for celciusItem in celciusArray {
    kelvinArray.append(celciusItem + 273)
}

As you see this is so complicated to write. Imagine this you can convert it in a single line. You can achieve it with map(_ :) function.

# MARK: - formula: T(K) = T(°C) + 273

var celciusArray = [10,20,30,-10,45]
var kelvinArray = celciusArray.map { $0 + 273 }

This is as easy as possible scenario and in Swift world you can see map everywhere.

The time complexity of the map is O(n) because of its looked at all of the element of the collection / sequence.

Filter

Filter is one of the best functionalities for filtering an collection type. As you can see below, the definition of the **filter(_ :)** function.

func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> [Self.Element]

When you use filter, you have to describe the isIncluded conditions. In callback of filter, will be filtered any item which returns true from the checking conditions.

For example, you have an struct named as Plane

struct Plane{
    var model: String
    var maxAltitude: Int
}

var planeArray = [
            Plane(model: "Airbus", maxAltitude: 1000),
            Plane(model: "Boeing", maxAltitude: 2000),
            Plane(model: "Airbus", maxAltitude: 3000),
            Plane(model: "Boeing", maxAltitude: 4000),
            Plane(model: "Airbus", maxAltitude: 5000),
            Plane(model: "Boeing", maxAltitude: 6000),
]

Maybe you want to filter the planeArray with just maxAltitude is bigger than 3500. With filter(_ :) function you can achive just like this.

let filteredArray = planeArray.filter{ $0.maxAltitude > 3500 }

The filteredArray will contains only Plane instances with maxAltitude > 3500. Just simple as it looks.

The time complexity of the map is O(n) because of it will be travel all elements of the Collection for filter.

Reduce

reduce(_: _:) Reduce in Swift language returns the result of the closure which let you combine elements in the Collection. The function definition is in below.

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

initialResult is the value of the combining initial value. Because of the reduce(_:, _:) function is generic, the initialValue type must be the same type with nextPartialResult. nextPartialResult returns the value of the closure which you can handle it. If you return a string from an array, you set initialResult a String and the result of the nextPartialResult closure must return String or if same scenario with start Int you must return Int from the nextPartialResult closure.

Let’s give an example to return a String from a Int collection.

let integers = [1,2,3,4,5]
let result = integers.reduce("Numbers are : ", { "\($0)" + " - " + "\($1)" })

The result array will return “Numbers are 1 - 2 - 3 - 4 - 5”

And maybe you may want to sum the array of Int.

let integers = [1,2,3,4,5]
let result = integers.reduce(0, { $0 + $1 })

The result will be the sum of the array.

The complexity of the Reduce is O(n) because it has a loop over the Collection which it’s used from index 0 to index N(lenght of collection).

FlatMap

FlatMap is a generic function

func flatMap<SegmentOfResult>(_ transform: (Self.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence

In Apple documentation flatMap described with this sentence; Returns an array containing the concatenated results of calling the given transformation with each element of this sequence. As it described flatMap convert list of lists to single list structure.

For example you have an array like below

let tryIt = [[1,2,3],[4,5]]
print("flat map result > ", tryIt.flatMap{$0})

# flat map result >  [1, 2, 3, 4, 5]

As you can see above, the list of list structure mapped to the single list structure via help of flatMap. And you have to learn another key thing of the flatMap. It contains nil values too. For example you have an array like below

let tryIt = [[1,2,3],[4,5,nil]]
print("flat map result > ", tryIt.flatMap{$0})

# flat map result >  [Optional(1), Optional(2), Optional(3), Optional(4), Optional(5), nil]

As you can see the result is single list structure but every value is Optional. Why? Because flatMap checks the collection is contains any Optional or not? If it contains optional, the result will be type of Optional. That’s it. But Apple recommends that when you work with a collection contains Optional you better use CompactMap rather than flatMap.

The complexity of the flatMap is O(N + M) where N is the source collection size and M is the result array size.

CompactMap

CompactMap as same as flatMap but with a difference. If a collection contains nil then it can be better to use CompactMap.

var singleList = [1,2,nil,4]
print("FLAT > ", singleList.flatMap({$0}))
print("COMPACT > ", singleList.compactMap({$0}))

# FLAT > [1,2,4]
# COMPACT > [1,2,4]

It seem same but flatMap deprecated in Optional sequences. So you better to use compactMap.

flatMap & compactMap

Let’s create a scenario to use them together. You have an array of arrays which contains nil.

let tryIt = [[1,2,3],[4,5,nil]]
let flatResult = tryIt.flatMap{$0}
// flatResult = [Optional(1), Optional(2), Optional(3), Optional(4), Optional(5), nil]
let compactResult = flatResult.compactMap{$0}
// compactResult = [1, 2, 3, 4, 5]

/*
    or you can just chain them with single line tryIt.flatMap{$0}.compactMap{$0}
*/

As you see, it’s good to use in different scenarios to handle collections in many way.

I hope you like the article.

Happy coding 🧑‍💻 🎮