Filter
Introduction
Welcome to the legit lesson of Functional Programming in Swift. As I’ve mentioned, there are a lot more than 4 functions besides filter, map, reduce, and flatmap in the functional programming world. However, the purpose isn’t to memorize all. It is to understand what goes under the hood so that nobody has to become a zombie. Today, let’s take a look at one popular function called, filter which is used to “filter” a group of objects.
Problem
How to get things done with one line of code?
Note: Most people use Swift functions without knowing the behind implementation
Imperative/Non-functional
Let’s attempt to grab even numbers using a non-functional approach.
1. // Get even numbers
2. var evens = [Int]()
4. for i in 1...10 {
5. if i % 2 == 0 {
6. evens.append(i)
7. }
8. }
It certainly works. But, you know what I’m about to say.
Declarative/Functional
If you remember correct, Array is just a struct and it contains built-in methods. filter is one of them. Let us utilize the filter function to get things done. You don’t have to understand what goes under the hood at this point, but you will by creating our own custom filter function.
1. Array(1...10).filter { (number) in number % 2 == 0 }
2. Array(1...10).filter { $0 % 2 == 0 }
Let us find out what the heck is going on. Don’t you worry. As long as you are comfortable with closures and generics, you will get through with me.
Become an A Student
My mom used to love (still do since I’m in college) when I handed my grade sheet filled with “A”s. Let’s find out how to only get “A” letters to please my mom.
1. let recentGrade = ["A", "A", "A", "A", "B", "D"] // It can be any data
Imperative/Non-functional
In the past, you probably have done something like this as shown below.
1. var happyGrade: [String] = []
3. for grade in recentGrade {
4. if grade == "A" {
5. happyGrade.append(grade)
6. } else {
7. print("My mama not happy")
8. }
9. }
10. print(happyGrade) // ["A", "A", "A", "A"]
The code above tells you the steps how you filtered recentGrade. This is long, boring, and confusing. You’ve created arbitrary instances such as happyGrade and grade which other developers must interpret. It consumes more calories which isn’t good for executing a simple task.
Functional Way
From now on, you might find it difficult. Bear with me. First of all, let’s create a function either returns true or false based on the parameter.
Create closure block that returns true/false
1. var isMomHappy: (String) -> Bool = { grade in return grade == "A" }
2. isMomHappy = { $0 == "A" }
4. isMomHappy("A") // true
5. isMomHappy("B") // true
Create a function that take the closure block whose type is (String) -> Bool. I will call the parameter as operation.
1. func simpleStringFilter(grades: [String], operation: (String) -> Bool) {
2. var happyGrade: [String] = []
3. for grade in grades {
4. if operation(grade) {
5. happyGrade.append(grade)
6. }
7. }
8. }
Within the simpleStringFilter, the operation closure is executed. If operation returns true, append the grade to happyGrade.
Let’s execute simpleStringFilter. The isMomHappy closure is passed to the operation parameter.
1. simpleStringFilter(grades: recentGrade, operation: isMomHappy) // ["A", ...]
The entire function may also return [String]
1. func stringFilter(grades: [String], operation: (String) -> Bool) -> [String] {
2. var happyGrade: [String] = []
3. for grade in grades {
4. if operation(grade) {
5. happyGrade.append(grade)
6. }
7. }
8. return happyGrade
9. }
Execute stringFilter.
1. let myGrade = ["A", "A", "A", "A", "B", "D"]
2. let happyGrade = stringFilter(grades: myGrade, operation: isMomHappy)
3. happyGrade // ["A", ...]
Pass Closure Directly
So far, you’ve passed the closure block indirectly to the operation parameter using isMomHappy. However, you may directly enter a closure block. The syntax may look strange but I’m sure you are comfortable with closures at this point.
1. stringFilter(grades: myGrade, operation: { element in return element == "A" })
2. stringFilter(grades: myGrade, operation: { $0 == "A" })
Generic Function
If you remember from Chapter 1, intro to generics, it doesn’t make sense to create a distinct function just for String only. Let us utilize the Swift generics.
1. func bobFilter<Bob>(array: [Bob], operation: (Bob) -> Bool) -> [Bob] {
2. var result: [Bob] = []
3. for element in array {
4. if operation(element) {
5. result.append(element)
6. }
7. }
8. return result
9. }
The placeholder type of bobFilter is Bob which should be defined later. Usually, we use T instead.
1. func myFilter<T>(array: [T], operation: (T) -> Bool) -> [T] {
2. var result: [T] = []
3. for element in array {
4. if operation(element) {
5. result.append(element)
6. }
7. }
8. return result
9. }
Examples
So, let us find out how myFilter can be used.
Ex 1) Filter Numbers
1. let AStudent = myFilter(array: Array(1...100), operation: { $0 >= 93 && $0 <= 100 })
2. print(AStudent) // [93, 94, 95, ... 100]
Ex 2) Vote Counting
1. let answer = [true, false, true, false, false, false, false]
2. let trueAnswer = myFilter(array: answer, operation: { $0 == true })
The rest is history. You can use your imagination from now on.
You may also utilize trailing closures to beautify.
1. let falseAnswer = myFilter(array: answer) { $0 == false }
Built-in Swift Filter
As I mentioned earlier, Array<T> comes with its own filter. Let us utilize. We don’t have to reinvent.
1. let zeroToHund = Array(1...100)
2. zeroToHund.filter{ $0 % 2 == 0 }.filter { $0 <= 50 }
3. // [2, 4, 6, 8, 10, 12, 14, ..., 50]
The Purest Form
The built-in filter may look something like this. Since you have not taken generics protocols in the following chapter and Advanced Swift, you may be confused with Element. It’s the type of an array for now.
1. extension Array {
2. func myFilter(_ logic: (Element) -> Bool) -> [Element] {
3. var result: [Element] = []
4. for item in self {
5. if logic(item) {
6. result.append(item)
7. }
8. }
9. return result
10. }
11. }
Let us test it out.
1. let result = Array(1...100).myFilter { $0 % 2 == 0 }
Source Code
6002_filter.playground
Resources
Intro to Functional Programming
Conclusion
Congratulations. You’ve learned what goes under the hood of filter, one of the most common functions. From now on, you understand what it means to pass a closure block at the end and you’ve learned how the closure block is used within the main function. This principle applies in every other major functions we will take a look at. Along with generics, it allows reusability, thus scalability in your code base.
At the end of the lesson, I’ve mentioned Element of an array. I don’t expect you to know what it is. However, in the last chapter of this course, you will learn the behind scene by understanding the statement, “Swift is a protocol oriented programming language”. Let’s continue.
