Swift enum pattern matching with extra conditions
In this post we are going to explore how we can provide more precise conditions for pattern matching when working with enums in Swift. The most common use case is using switch
statements with the where
clause to get more control over case conditions. But we'll also look into using the where
clause in for-in
loops to avoid unnecessary extra iterations. And finally, we'll see how to add extra conditions in while
loops and if-case
statements when the where
clause is not available.
Let's consider an enum TransportationEvent
representing different types of events in a transportation system.
enum TransportationEvent {
case busArrival(busNumber: Int, passengers: Int)
case trainArrival(trainNumber: Int, passengers: Int, cargoLoad: Int)
case bicycleArrival
}
Now, imagine we have a system that handles these events. A basic switch statement might look like this.
let event = TransportationEvent.busArrival(busNumber: 42, passengers: 15)
switch event {
case .busArrival(let busNumber, let passengers):
print("Bus \(busNumber) arrived with \(passengers) passengers.")
case .trainArrival(let trainNumber, let passengers, let cargoLoad):
print("Train \(trainNumber) arrived with \(passengers) passengers and \(cargoLoad) tons of cargo.")
case .bicycleArrival:
print("A bicycle arrived.")
}
This works, but what if we want to differentiate events based on the number of passengers or the amount of cargo? That's when we'd need to add some extra conditions.
where
clause in switch
statements
We can utilize the where
clause to add specific conditions within our cases in a switch
statement in Swift. In our TransportationEvent
enum example it can help us to differentiate events based on the number of passengers or the amount of cargo.
switch event {
case .busArrival(let busNumber, let passengers) where passengers < 10:
print("Bus \(busNumber) arrived with only a few passengers.")
case .busArrival(let busNumber, let passengers):
print("Bus \(busNumber) arrived with \(passengers) passengers.")
case .trainArrival(let trainNumber, let passengers, let cargoLoad) where cargoLoad > 50:
print("Train \(trainNumber) arrived with \(passengers) carrying a heavy cargo load.")
case .trainArrival(let trainNumber, let passengers, let cargoLoad):
print("Train \(trainNumber) arrived with \(passengers) passengers and \(cargoLoad) tons of cargo.")
case .bicycleArrival:
print("A bicycle arrived.")
}
where
clause in for-in
loops
In addition to switch
statements, the where
clause can also be leveraged in a for-in
loop to iterate over elements that meet specific conditions. When iterating through a collection or sequence, the where
clause enables us to filter the elements and iterate only over those that satisfy particular criteria. This allows us to process or manipulate the desired subset of elements within the loop.
For example, let's consider the TransportationEvent
enum we've been working with. Suppose we have an array of transportation events, and we want to iterate over only the busArrival
events where the number of passengers is greater than 10. We can achieve this by using the where
clause in a for-in
loop as follows.
let transportationEvents: [TransportationEvent] = [
.busArrival(busNumber: 1, passengers: 5),
.busArrival(busNumber: 2, passengers: 15),
.trainArrival(trainNumber: 10, passengers: 50, cargoLoad: 100),
.busArrival(busNumber: 3, passengers: 20),
.bicycleArrival
]
for case let .busArrival(busNumber, passengers) in transportationEvents where passengers > 10 {
print("Bus \(busNumber) arrived with \(passengers) passengers.")
}
In this example, the where
clause is used in the for-in
loop to filter out the busArrival
events where the number of passengers is greater than 10. Only the busArrival
events with more than 10 passengers will be processed within the loop.
Extra conditions in while
loops
To add extra conditions in while
loops we can use the comma syntax instead of the where
clause. The comma essentially behaves like a logical AND operator, ensuring all conditions are met.
So if we want to carry on processing transportation events while they are bus arrivals with some passengers and stop as soon as our conditions are not met, we can write the following loop.
var index = 0
while case let .busArrival(busNumber, passengers) = transportationEvents[index], passengers > 0 {
print("Processing bus \(busNumber) arrival with \(passengers) passengers.")
index += 1
}
In this example, the while
loop specifies two conditions separated by a comma. The first one checks if the current element at transportationEvents[index]
matches the busArrival
case, and the second one ensures that the number of passengers is greater than 0. Only while both conditions are met, the loop will iterate and process the event.
Extra conditions in if-case
statements
If we need to add extra conditions in control flow statements, such as if-case
, we can use the same comma syntax we used with a while
loop. Let's say we want to check if a single transportation event is a busArrival
with more than 10 passengers. We can accomplish this using the if-case
statement, as demonstrated below.
let event = TransportationEvent.busArrival(busNumber: 42, passengers: 15)
if case let .busArrival(busNumber, passengers) = event, passengers > 10 {
print("Processing bus \(busNumber) arrival with \(passengers) passengers.")
}
The comma-separated condition ensures that the busArrival
event has more than 10 passengers before executing the code block. This allows us to conditionally handle specific scenarios based on our pattern matching criteria.
With the techniques we explored in this post, we can add a layer of flexibility and expressiveness to our pattern matching cases in Swift. Whether we are using switch
statements, loops or control flow statements like if-case
, we can handle complex scenarios with precision and clarity.
As someone who has worked extensively with Swift, I've gathered many insights over the years. I've compiled them in my book Swift Gems, which is packed with advanced tips and techniques to help intermediate and advanced Swift developers enhance their coding skills. From optimizing collections and handling strings to mastering asynchronous programming and debugging, "Swift Gems" provides practical advice to elevate your Swift development. Grab your copy of Swift Gems and let's explore the advanced techniques together.