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]()
  3. for i in 1...10 {
  4. if i % 2 == 0 {
  5. evens.append(i)
  6. }
  7. }

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] = []
  2. for grade in recentGrade {
  3. if grade == "A" {
  4. happyGrade.append(grade)
  5. } else {
  6. print("My mama not happy")
  7. }
  8. }
  9. 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" }
  3. isMomHappy("A") // true
  4. 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.