Welcome to the fourth post in the series about higher order functions in Swift. Today I’m going to present more of these functions available to use in Swift, which, like all the rest, can really help us write cleaner and shorter code.
Before I dive into the main content of the post, here’s a summary of the previous articles on that series:
Today on the menu are the reduce
, contains
, allSatisfy
and removeAll
higher order functions. Let’s go through them one by one.
The reduce higher order function
The reduce
higher order function has a simple purpose; to produce a single value out of the elements of a collection, based on calculations that we provide it with.
Let’s see what that means and how it works using a straightforward example. Suppose that we have the following array with the average temperatures per day during a week (in Celsius degrees):
1 2 3 |
let temperatures = [15, 17.3, 16.4, 18.1, 17.9, 16.5, 17.1] |
In the end of the week we want to calculate the average temperature of all days. To do that, it’s necessary to get the sum of all temperatures and then divide by the number of days (7). A custom implementation to perform that calculation would look like this:
1 2 3 4 5 6 7 |
var sum: Double = 0 for temp in temperatures { sum += temp } let avg = sum / 7 |
Although that’s not wrong, we can do much better using the reduce
higher order function, and eventually end up with on line only, instead of five that we have here.
Let’s see reduce
at its expanded form first:
1 2 3 4 5 |
let avg = temperatures.reduce(0) { (prevResult, currentTemp) -> Double in prevResult + currentTemp } / 7 |
Like any other higher order function, reduce
is accessible through the collection object; that’s the temperatures
array here. See that the result that returns back is divided by 7 in order to calculate the proper average temperature.
reduce
accepts two arguments; the first one is the initial value that calculations will start with, and you can think of it like the initialization of the sum
value above (sum = 0
).
The second argument is a closure with two arguments as well. The first is the partial result of the calculations on each iteration, and the second is the value of the current item in the collection.
Instead of using actual arguments in the closure and have a return type, we can use the shorthand arguments instead, and shorten the above code. We do that like so:
1 2 3 |
let avg = temperatures.reduce(0, { $0 + $1 }) / 7 |
The $0
argument is the partial result on each iteration, and the $1
is the current item in the temperatures
array that is being added to the partial sum.
See that with a single line only we can achieve exactly what we achieved with five lines in the original implementation. But reduce
can become even simpler when we have basic calculations, just like the one in this example. We can replace the closure containing the calculation formula with the symbol of the operation that we want to perform:
1 2 3 |
let avg = temperatures.reduce(0, +) / 7 |
Amazingly simple, right?
We can use reduce
with dictionaries as well, and pretty much the way we use them with arrays. Think of the temperatures again, but this time we have them as values next to day names into a dictionary:
1 2 3 4 5 |
let dayTemps = [“Monday”: 15, “Tuesday”: 17.3, “Wednesday”: 16.4, “Thursday”: 18.1, “Friday”: 17.9, “Saturday”: 16.5, “Sunday”: 17.1] |
Calculating the average temperature of the week is similar to what we already previously met with the $0
and $1
arguments. The only difference this time is that we need to explicitly access the value
of the $1
argument, as the $1
is a tuple on its own, containing each key – value pair. In code:
1 2 3 |
let avgTemp = dayTemps.reduce(0, { $0 + $1.value }) / 7 |
That’s all about the reduce
higher order function. It’s easy to use it, with the most important thing to take care of probably being the initial value that you will provide it with.
The contains higher order function
The contains
higher order function helps to easily determine if the items of a collection satisfy a given condition or not, returning a Boolean value as a result.
contains
is a really handy function, as quite often is necessary to give to the execution of a program a different path, depending on the results of some condition applied to the elements of a collection. To see how it works, suppose that the following array contains all languages that an app has been translated to:
1 2 3 |
let languages = [“English”, “French”, “Spanish”, “Greek”, “Chinese”, “Turkish”] |
Say now that we want to check if a certain language exists or not in the collection. For the sake of the example, let’s say that we’re looking for the existence of the German language. The condition in that case is to check if any of the array items is equal to the “German” value.
Let’s see first how we would be performing that check using a for-in
loop:
1 2 3 4 5 6 7 8 9 |
var germanExists = false for language in languages { if language == “German” { germanExists = true break } } |
Simple, yet too many lines for such a simple task. Let’s see now how we would perform the same with the contains
function:
1 2 3 4 5 |
let germanExists = languages.contains { language -> Bool in return language == “German” } |
The closure of the function has one argument that represents each item in the original collection. Note that its return value is always a Boolean value. The closure’s body is the place to write the custom condition that we want to determine if any of the items satisfy.
As it happens with all higher order functions, contains
has a shorter version as well:
1 2 3 |
let germanExists = languages.contains { $0 == “German” } |
Once again the $0
shorthand arguments comes to replace the explicit parameter name and make the whole syntax shorter and more clear. The only thing required between the curly brackets is the condition.
Let’s go through another example now, and for that we will use the dictionary with the temperatures from the previous part. Let’s assume that we want to check if there was at least one day with a temperature less than 10 degrees. With contains
that’s as easy as shown next:
1 2 3 |
let lowTemp = dayTemps.contains { $0.value < 10 } |
Or, we could check if there are days with temperature more than 17 degrees:
1 2 3 |
let highTemp = dayTemps.contains { $0.value > 17 } |
Note once again that all the above return a true or false value; not the actual items that satisfy the condition. If you want to get the actual items then you should use the filter
higher order function.
The allSatisfy higher order function
A similar to contains
is the allSatisfy
higher order function. It works exactly just like the former one, returning a Boolean value as a result. However, there is a great difference; it returns true if only all items in the collection satisfy the given condition, otherwise it returns false. That’s contrary to contains
, which returns true if at least one item in the collection satisfies the condition.
Let’s assume that the following array contains the grades of a student in five lessons as a percentage value (0 – 100):
1 2 3 |
let grades = [85, 74, 90, 64, 82] |
The example scenario is to determine whether the student passes the base to all lessons, meaning that each grade is over 50%. We can use allSatisfy
function to achieve that:
1 2 3 4 5 |
let pass = grades.allSatisfy { grade -> Bool in return grade > 50 } |
The closure’s argument matches to each grade on the original array while iterating through it. The return value, similarly to contains
, is always a Boolean value, while the closure’s body is the place to write the condition.
The shorter version of the above is this:
1 2 3 |
let pass = grades.allSatisfy { $0 > 50 } |
allSatisfy
is equally handy and useful to contains
, just keep their difference in mind and use it when necessary.
The removeAll higher order function
The removeAll
higher order function is quite useful when you want to remove items from a collection based on a given condition. It’s not meant to be used in order to remove elements at specific positions, as remove(at:)
method does. Instead, its purpose is to remove only those elements that satisfy the condition.
There are two things to note about removeAll
. The first is that it does not return a new collection, but it modifies the original one. Remember that if keeping the source collection is important to your program. The second is that removeAll
is available for arrays only, not for dictionaries or sets.
To see how to use it, let’s consider the grades
array from the previous part, but with one difference; we mark it with the var
keyword so it can be mutated:
1 2 3 |
var grades = [85, 74, 90, 64, 82] |
Let’s suppose that we want to remove all items that are less than 75, so we keep only those grades higher than that mark. Using the removeAll
function in its expanded form we can do so like that:
1 2 3 4 5 |
grades.removeAll { grade -> Bool in return grade < 75 } |
Note that the condition should be satisfied by the items to remove, not those that should remain to the array.
Just like the last two higher order functions previously presented, the closure’s body is the place to write the custom condition. The above can be written even shorter:
1 2 3 |
grades.removeAll { $0 < 75 } |
Printing the grades
array after the above will give us this:
1 2 3 |
[85, 90, 82] |
As you can see, using the removeAll
function is equally easy as the other higher order functions previously demonstrated. It’s a really useful tool that can save us from unnecessary coding when it’s necessary to perform tasks that match to its purpose.
Summary
In this post I presented and talked about another set of higher order functions in Swift. Like all other functions from the previous posts, those are great tools to know about and use, as they can lead to shorter, cleaner, and more readable and maintainable code. I hope you found valuable information to the content of this post today. Thank you for reading!