Higher Order Functions in Swift (Sorted, Map, Filter, Reduce)

Ario Liyan
7 min readDec 31, 2022

--

My friend Ahmad and I decided to test ourselves with higher-order functions in Swift. We created a Playground and defined a scenario for ourselves and tried to answer the questions that we arbitrarily created. In this post, I’m going to explain both the questions and answers. If you have free time to spare, let’s jump in. Note that this is not an article to learn about Higher Order Functions but rather just to practice them(You can learn them via these links: link 1 and link 2).

It’s me trying to solve the questions and mistakenly checking the wrong value to compare to the answer…
import UIKit

struct Student: Equatable {
var name: String
var age: Int
var grade: Int
var isMale: Bool

static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.age == rhs.age
}

static func > (lhs: Self, rhs: Self) -> Bool {
return lhs.age > rhs.age
}
}

struct Class {
var name: String
var students: [Student]
}


var classA = Class(name: "Class A", students: [
Student(name: "Jack", age: 10, grade: 20, isMale: true),
Student(name: "Mack", age: 11, grade: 19, isMale: true),
Student(name: "Rock", age: 10, grade: 20, isMale: true),
Student(name: "Mock", age: 10, grade: 18, isMale: true),
Student(name: "Rossa", age: 11, grade: 17, isMale: false),
Student(name: "Julia", age: 10, grade: 20, isMale: false),
Student(name: "Britney", age: 10, grade: 19, isMale: true),
])

var classB = Class(name: "Class B", students: [
Student(name: "Hamid", age: 10, grade: 20, isMale: true),
Student(name: "Naser", age: 11, grade: 19, isMale: true),
Student(name: "Jamshid", age: 10, grade: 20, isMale: true),
Student(name: "Azhman", age: 10, grade: 18, isMale: true),
Student(name: "Nastaran", age: 11, grade: 17, isMale: false),
Student(name: "Golshifteh", age: 10, grade: 20, isMale: false),
Student(name: "Mozhgan", age: 10, grade: 19, isMale: true),
])

We have two structs, Class, and Student, as you see above, the “Student” struct contains data about students including name, age, grade, and gender. For the sake of the questions, we decided that the “Student” struct should conform to the “Equatable” protocol so we can compare different instances of the struct. For comparison, we defined the “greater than” sign and the “Equality operator”. Both functions should implement a logic to decide and return a “Boolean” to answer the relative operator….

“Class” struct represents a class in the real world, it includes a name and array of students.

Note: In all questions, our goal is not only to solve the problem but also to do it in only one line of code.

Are you ready?

Question 1❓: We want an array including all boys’ names.

Key 🔑: The answer should be an array as shown below:

["Jack", "Mack", "Rock", "Mock", "Britney", "Hamid", "Naser", "Jamshid", "Azhman", "Mozhgan"]

Guide 🦮: You should use the “filter”, and “compactMap” functions.

Answer 💡:

Step one:
Let’s think about the question, the first thing to be considered is that we want all the boys, so we need to join both arrays together to do so we can use the addition operator + like shown below:

let universalArray = classA.students + classB.students

Note that the “classA” and “classB” are objects of type Class that we want their “students” arrays to be joined.

Step two:
Now that we have all the students in the “universalArray” we need to filter them based on their gender, because as the question asked us we want to have all the boys’ names.

let allBoys = universalArray.filter{ student in
if student.isMale{
return true
}
return false
}

//OR

let allBoys = universalArray.filter({$0.isMale == true})

The “filter” function is like a “foreach” that iterates, on our collection and filters the values based on what we have asked it, here it goes through the “universalArray” and then checks each value, and if there are male it returns true and adds the value to the “allBoys” array.

The second style of writing the “filter” function is concise and less readable but you can write it that way too, it’s on you to choose the style that fits your character.

Step three:
It is time to extract the names from the student objects and add them all to an array of strings. To do so we use the “compactMap” function.

let allboysnames = allBoys2.compactMap({Student in
Student.name
})

//OR

let allBoysNames = allBoys.compactMap({$0.name})

The “compactMap” function iterates on our collection and takes the parameter that we want and add the to the “allboysnames” array.

Step four:
The last step is to convert our code to a code that runs only on one line

var allBoysNames = (classA.students + classB.students).filter({$0.isMale == true}).compactMap({$0.name})

Question 2❓: what is the total age of all students?

Key 🔑: The answer should be the given number

144

Guide 🦮: You should use the “filter”, and “reduce” functions.

Answer 💡:

Step one:
As we now have the experience of the previous question we know how to get all the students’ ages in an array and it’s like below:

let allStudentAge = (classA.students + classB.students).compactMap({$0.age}

Step two:

Now we need to combine and calculate all the students’ ages, for this matter we can use the “reduce” function and as the documentation says:

The reduce function Returns the result of combining the elements of the sequence using the given closure.

let totalAgeOfAllStudents = allStudentAge..reduce(0, +)

Note that the reduce function has a couple of overloads with different signatures in this overload we need to give the function two inputs one the initial value that we want our numbers to be added to, and the operator that we want to apply to our array. The reduce function here goes through our array and adds each value to the next and finally, it returns the total value.

Question 3❓: We want all the students’ names sorted by age.

Key 🔑: The answer should be an array similar to the one given below

["Mack", "Rossa", "Naser", "Nastaran", "Jack", "Rock", "Mock", "Julia", "Britney", "Hamid", "Jamshid", "Azhman", "Golshifteh", "Mozhgan"]

Guide 🦮: You should use the “sorted”, and “compactMap” functions.

Answer 💡:

Step one:
The first step is to join the students into an array.

let students = (classA.students + classB.students)

Step two:
We need to sort them based on their age now. To do so we use the “sorted” function.

let studentsSortedByAge = students.sorted(by: {$0.age > $1.age})

The “sorted” function takes a collection and iterates on it and compares its indexes and sorts them based on what you have asked. Here it iterates through the “students” array and sorts them descendingly.

Step three:
Now it’s time to extract the names and store them into an array, for this matter we shall use “compactMap”.

let allStudentsSortedByAge = studentsSortedByAge.compactMap({$0.name})

//The answer in one line...
let allStudentsSortedByAge = (classA.students + classB.students).sorted(by: {$0.age > $1.age}).compactMap({$0.name})

Question 4❓: We want random students’ names…

Key 🔑: The answer should be an array of arbitrary length containing the students' names…

Guide 🦮: You can use both shuffled and “randomElement” functions…

Answer 💡: We will take both approaches but we first go with the “randomElemen”t approach…

As the question didn’t specify whether the names should be unique or not, we consider the answer to having duplicated names(for only “randomElement” approach).

The first thing that we want the question to give us is the number of wanted names, let’s say it’s 5.

So we want 5 random names…

For this approach(using “randomElement”), we define an array with the range of 1 to the wanted number in our case 5.

As the “compactMap” iterate through the array, so we can call the “compactMap” to act as our loop and then we extract random names in the closure body of the function. We can be sure that our code within the closure will be executed 5 times. Within the closure, we call the “randomElement” on the array of student names…

Step 1:
In this step, we defined an array including 5 indexes. Note that the 1…5 means from 1 to 5 in other words it creates an array with 5 indexes each index contains relative numbers from 1 to 5.

let randomStudentNumbers = Array(1...5)

Step 2:
The second step will be calling the “compactMap” on the array so it’ll iterate 5 times and execute the code within its closure five times…

Note that we just want to use the compact map as a loop and we do this because we wanted to answer the question in only one line.

var randomMaleStudentsNames = Array(1...5).compactMap({_ in /*The code
within this closure will be executed 5 times */})

Step 3:

Now it’s time to create an array of students’ names and call the “randomElement” on it.

let randomMaleStudentsNames = Array(1...2).compactMap({_ in
(classA.students + classB.students).compactMap({$0.name}).randomElement()!})

As For the “shuffled” approach, we can guarantee that we’ll return unique names…

let arrayOfNames = (classA.students + classB.students)
.compactMap({$0.name})shuffled()[0...4]

--

--

Ario Liyan

As an iOS developer with a passion for programming concepts. I love sharing my latest discoveries with others and sparking conversations about technology.