A function is a reusable piece of code designed to avoid repetitive code and improve code quality. Although not a purely functional language but more of general-purpose programming language, Swift still empowers us with higher-order functions that implements functional principles in the development of elegant, and concise code.
What’s a Higher Order Function?
A higher order function is a function that takes another function or closure as an argument, and returns a function or closure as it output. In contrast to first order functions, which don’t take or return a function as an argument or output.
Why Use Them?
Their ability to operate on other functions both as arguments or as return outputs, provides us with functional principles that we can use to abstract repetitive logic from our code.
Higher order function can be used to perform transformative operations on elements in collections like arrays, sets and dictionaries.
Some of the best-known built in higher order functions in swift are map, filter and reduce.
Higher Order Functions in Action
Map
The map function works by performing an operation on all the elements of a collection and returns a new collection with the results of that operation.
Concrete Example 🥗
To understand map even better, let us connect it to a more concrete example in the real world. Let’s say you have a collection of fruits and you want to slice each fruit into a small chunk. By applying the map function on your collection, you will have a new basket of sliced fruits.
Note Unlike the fruit scenario, in programming the new collection will be of the same length or item count as the initial collection.
Code Example 🧑🏽💻
For example, let’s say that we have an array of numbers and we want to obtain a new array with each of the numbers squared. We could do this by using a good old for…in loop to perform the operation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// Perform Square Operation on Array Without map
let listOfNumbers: [Int] = [1,2,3,4,5,10]
var squaredNumbers: [Int] = [Int]()
for number in listOfNumbers {
let squaredNum = number * number
squaredNumbers.append(squaredNum)
}
print("listOfNumbers: \(listOfNumbers) ","squaredNumbers \(squaredNumbers)")
///output: listOfNumbers: [1, 2, 3, 4, 5, 10] squaredNumbers [1, 4, 9, 16, 25, 100]
As you can see, we had to iterate over the array, perform the square operation on each element and then append the updated element to a new squaredNumbers’s array. This is quite tedious, and when applied over different logical operations across an entire code base you can see how such iterations can bloat up the codebase.
Same operation with map
1
2
3
4
5
6
7
8
9
/// Perform Square Operation on Array using map
let listOfNumbers: [Int] = [1,2,3,4,5,10]
let squaredNumbers = listOfNumbers.map{$0 * $0}
print("listOfNumbers: \(listOfNumbers) ","squaredNumbers \(squaredNumbers)")
///output: listOfNumbers: [1, 2, 3, 4, 5, 10] squaredNumbers [1, 4, 9, 16, 25, 100]
Using map, we iterate over the array, perform the square operation on each element and then return a brand new array that satisfy our operative condition.
Note: The $0 syntax is a shorthand argument that refers to any element in the array.
Filter
As the name suggests, the filter higher order function helps us filter the content of a collection and returns a new collection that contains elements that meet a specified condition.
Concrete Example 🥗
To understand filter even better, let us map it to a more concrete example in the real world. Let’s say you have a collection of apple, orange and banana fruits and you want to get just the oranges into a new basket. By applying the filter function on your collection of fruits you will have a new basket of orange fruits that excludes every other fruits that was in the initial collection, now you can make that yummy glass of orange juice🥤 you’ve been craving.
Code Example 🧑🏽💻
Let’s suppose that we have an array of country names and we want to filter out the ones that contain land. Without using the filter method we would have to do something like that:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/// Perform Filter Operation on Array without the filter higher order function
let countries: [String] = ["ALBANIA",
"BOLIVIA",
"CANADA",
"DENMARK",
"ETHIOPIA",
"FINLAND",
"GERMANY",
"HUNGARY",
"IRELAND",
"JAPAN",
"KENYA"]
var countriesWithLand: [String] = [String]()
for country in countries {
if country.contains("LAND"){
countriesWithLand.append(country)
}
}
print("countriesWithLand: ", countriesWithLand)
///output: countriesWithLand: ["FINLAND", "IRELAND"]
Here’s how we can accomplish the same operation using filter function’s shorthand argument:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// Perform Filter Operation on Array using the filter higher order function
let countries: [String] = ["ALBANIA",
"BOLIVIA",
"CANADA",
"DENMARK",
"ETHIOPIA",
"FINLAND",
"GERMANY",
"HUNGARY",
"IRELAND",
"JAPAN",
"KENYA"]
var countriesWithLand = countries.filter{$0.contains("LAND")}
print("countriesWithLand: ", countriesWithLand)
///output: countriesWithLand: ["FINLAND", "IRELAND"]
Reduce
Reduce is a higher order function that, when applied on a collection, produces one value from the values of all elements in the collection. I know that’s a mouthful so let me simplify it. Think of reduce as a function that combines all the elements in an array into a singular element.
You have an array of items and you want to compute some new value by iterating over each item. The result can be anything, another array, a new object, a boolean value etc.
Concrete Example 🥗
To understand reduce even better, let us use a more concrete visual example that connects it to the real world.
Let’s say you have a collection of fruits and you want to combine them to make a fruit salad. By applying the reduce function on your collection of fruits you will have a delicious bowl of fruit salad as a result.
Code Example 🧑🏽💻
Let’s say we have an array of expenses and we want to calculate the total expense for the month. Without using the reduce method we would have to do something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
/// Perform Operation on Array without the Reduce higher order function
let expenses = [100, 200, 300, 400]
var totalExpensesInSeptember: Int = 0
for expense in expenses {
totalExpensesInSeptember += expense
}
print("totalExpensesInSeptember: ", totalExpensesInSeptember)
///output: totalExpensesInSeptember: 1000
Here’s how we can accomplish the same operation using reduce function’s shorthand argument:
1
2
3
4
5
6
7
8
9
/// Perform Reduce Operation on Array
let expenses = [100, 200, 300, 400]
var totalExpensesInSeptember = expenses.reduce(0, +)
print("totalExpensesInSeptember: ", totalExpensesInSeptember)
///output: totalExpensesInSeptember: 1000
Conclusion
It’s important to note that unlike their concrete examples the Map, Filter, and Reduce methods do not manipulate the original collection. In each case, you end up with a brand new value. These higher order functions introduces some elements of functional programming to swift. That helps us write clean abstract code that are reuseable by eliminating unnecessary for loops in our logical operations.