Filter in Swift

Hello, Swift developers! Today, we’re exploring another powerful higher-order function: filter. If you’ve ever needed to separate the wheat from the chaff in your collections, filter is about to become your new best friend.

What is Filter?

The filter method creates a new collection containing only the elements that satisfy a given condition. It’s like having a bouncer for your data party!

Basic Usage

Let’s start with a simple example:

Swift
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) 
// Output: [2, 4, 6, 8, 10]

Here, we’ve filtered out all the odd numbers, leaving us with only the evens.

Filtering Complex Types

Filter isn’t just for simple types. Let’s use it with a custom struct:

Swift
struct Student {
    let name: String
    let grade: Int
}

let students = [
    Student(name: "Alice", grade: 85),
    Student(name: "Bob", grade: 70),
    Student(name: "Charlie", grade: 90),
    Student(name: "David", grade: 65)
]

let highAchievers = students.filter { $0.grade >= 80 }
highAchievers.forEach { 
    print("\($0.name) is a high achiever!") 
}
// Output:
// Alice is a high achiever!
// Charlie is a high achiever!

Chaining Filter Calls

You can chain multiple filter calls for more complex filtering:

Swift
let multipleOfThreeAndEven = numbers.filter { $0 % 3 == 0 }
                                    .filter { $0 % 2 == 0 }
print(multipleOfThreeAndEven) 
// Output: [6]

Filter with Optionals

Swift’s filter works great with optionals too:

Swift
let optionalNumbers: [Int?] = [1, nil, 3, nil, 5]
let nonNilNumbers = optionalNumbers.compactMap { $0 }
print(nonNilNumbers) 
// Output: [1, 3, 5]

// Alternatively, you can use filter before compactMap
let filteredNumbers = optionalNumbers.filter { $0 != nil }
                                     .compactMap { $0 }
print(filteredNumbers) 
// Output: [1, 3, 5]

Filter with Dictionaries

Don’t forget, filter works on dictionaries too:

Swift
let grades = ["Alice": 85, "Bob": 70, "Charlie": 90, "David": 65]
let highGrades = grades.filter { $0.value >= 80 }
print(highGrades) 
// Output: ["Alice": 85, "Charlie": 90]

Filter with Strings

Strings are collections too! You can filter characters in a string:

Swift
let mixedString = "ab12cd34ef"
let onlyLetters = mixedString.filter { $0.isLetter }
print(onlyLetters) 
// Output: "abcdef"

Filtering Nested Collections

When dealing with nested collections, you might want to use filter in combination with flatMap:

Swift
let nestedArrays = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let evenInNestedArrays = nestedArrays.flatMap { $0 }
                                     .filter { $0 % 2 == 0 }
print(evenInNestedArrays) 
// Output: [2, 4, 6, 8]

Filter in the Real World

Let’s look at a more practical example. Say you’re building a task management app:

Swift
struct Task {
    let id: Int
    let title: String
    let isCompleted: Bool
}

let tasks = [
    Task(id: 1, title: "Buy groceries", isCompleted: false),
    Task(id: 2, title: "Finish report", isCompleted: true),
    Task(id: 3, title: "Call mom", isCompleted: false),
    Task(id: 4, title: "Gym workout", isCompleted: true)
]

let incompleteTasks = tasks.filter { !$0.isCompleted }
incompleteTasks.forEach { task in
    print("ToDo: \(task.title)")
}
// Output:
// ToDo: Buy groceries
// ToDo: Call mom

let completedTasks = tasks.filter { $0.isCompleted }
print("You've completed \(completedTasks.count) tasks!")
// Output: You've completed 2 tasks!

Performance Considerations

While filter is incredibly useful, be mindful of performance when working with large collections. If you only need to find the first matching element, consider using first(where:) instead:

Swift
let firstEvenNumber = numbers.first { $0 % 2 == 0 }
print(firstEvenNumber ?? "No even numbers found") 
// Output: Optional(2)

Wrapping Up

As you can see, filter is an incredibly powerful tool in your Swift arsenal. It allows you to sift through collections with precision, whether you’re working with simple types, complex objects, or nested structures.

Remember, like map, filter doesn’t modify the original collection – it always returns a new one. This makes it perfect for maintaining the integrity of your original data while extracting exactly what you need.

So the next time you find yourself writing a for-loop with a bunch of if-statements to extract certain elements from a collection, ask yourself: “Could I use filter here?” Chances are, you can – and your code will be all the cleaner and more expressive for it!


Posted

in