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.

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!

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.

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!

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]

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]

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]

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"

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]

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!

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