Using case and where statements in Swift iOS 15.08.2018

The case and where statements within a switch statement can be very powerful. Using case and where statements within conditional statements can also make our code much smaller and easier to read. Conditional statements, such as if, for, and while, can also make use of the where and case keywords. Let's look at some examples, starting off with using the where statement to filter the results in a for-in loop.

Filtering with the where statement

In this example, we will take an array of integers and print out only the even numbers. However, before we look at how to filter the results with the where statement, let's look at how to do this without the where statement:

for number in 1...30 {
    if number % 2 == 0 {
        print(number)
    }
}

In this example, we use a for-in loop to cycle through the numbers 1 to 30. Within the for-in loop, we use an if conditional statement to filter out the odd numbers. In this simple example, the code is fairly easy to read, but let's see how we can use the where statement to use fewer lines of code and make them easier to read:

for number in 1...30 where number % 2 == 0 {
    print(number)
}

We still have the same for-in loop as in the previous example. However, we have now put the where statement at the end; therefore, we only loop through the even numbers. Using the where statement shortens our example by two lines and makes it easier to read because the where clause is on the same line as the for-in loop, rather than being embedded in the loop itself.

Now let's look at how we could filter with the for-case statement.

Filtering with the for-case statement

In this next example, we will use the for-case statement to filter through an array of tuples and print out only the results that match our criteria. The for-case example is very similar to using the where statement where it is designed to eliminate the need for an if statement within a loop to filter the results. In this example, we will use the for-case statement to filter through a list of World Series winners and print out the year(s) a particular team won the World Series:

var worldSeriesWinners = [
    ("Red Sox", 2004),
    ("White Sox", 2005),
    ("Cardinals", 2006),
    ("Red Sox", 2007),
    ("Phillies", 2008)
]

for case let ("Red Sox", year) in worldSeriesWinners {
    print(year)
}

In this example, we created an array of tuples named worldSeriesWinners, where each tuple in the array contained the name of the team and the year that they won the World Series. We then use the for-case statement to filter through the array and only print out the years that the Red Sox won the World Series. The filtering is done within the case statement, where ("Red Sox", year) states that we want all the results that have the string Red Sox in the first item of the tuple, and the value of the second item in the year constant. The for-in loop then loops through the results of the case statement, printing out the value of the year constant.

The for-case-in statement also makes it very easy to filter out the nil values in an array of optionals. Let's look at an example of this:

let myNumbers: [Int?] = [1, 2, nil, 4, 5, nil, 6]
for case let .some(num) in myNumbers {
    print(num)
}

In this example, we created an array of optionals named myNumbers that could contain either an integer value or nil. An optional is internally defined as an enumeration, as shown in the following code:

enum Optional<Wrapped> {
    case none,
    case some(Wrapped)
}

If an optional is set to nil, it will have a value of none, but if it is not nil it will have a value of some, with an associated type of the actual value. In our example, when we filter for .some(num), we are looking for any optional that has a non-nil value. As shorthand for .some(), we could use the ? (question mark) symbol, as we will see in the following example. This example also combines the for-case-in statement with a where statement to perform additional filtering.

let myNumbers: [Int?] = [1, 2, nil, 4, 5, nil, 6]

for case let num? in myNumbers where num > 3 {
    print(num)
}

This example is the same as the previous example, except that we have inputted the additional filtering with the where statement. In the previous example, we looped through all of the non-nil values, but in this example, we have looped through the non-nil values that are greater than 3. Let's see how we do this same filtering without the case or where statements:

let myNumbers: [Int?] = [1, 2, nil, 4, 5, nil, 6]
for num in myNumbers {
    if let num = num {
        if num > 3 {
            print(num)
        }
    }
}

Using the for-case-in and where statements can greatly reduce the number of lines needed. It also makes our code much easier to read because all the filtering statements are on the same line.

Let's look at one more filtering example. This time, we will look at the if-case statement.

Using the if-case statement

Using the if-case statement is very similar to using the switch statement. Most of the time the switch statement is preferred when we have over two cases we are trying to match, but there are instances where the if-case statement is needed. One of those times is when we are only looking for one or two possible matches, and we do not want to handle all the possible matches. Let's look at an example of this:

enum Identifier {
    case Name(String)
    case Number(Int)
    case NoIdentifier
}

var playerIdentifier = Identifier.Number(2)

if case let .Number(num) = playerIdentifier {
    print("Player's number is \(num)")
}

In this example, we created an enumeration named Identifier that contains three possible values: Name, Number, and NoIdentifier. We then created an instance of the Identifier enumeration named playerIdentifier, with a value of Number and an associated value of 2. We then used the if-case statement to see if the playerIdentifier had a value for Number and; if so, we printed a message to the console.

Just like the for-case statement, we can perform additional filtering with the where statement. The following example uses the same Identifier enumeration as we used in the previous example:

var playerIdentifier = Identifier.Number(2)
if case let .Number(num) = playerIdentifier, num == 2 {
    print("Player is either XanderBogarts or Derek Jeter")
}

In this example, we have used the if-case statement to see if the playerIdentifier had a value of Number, but we also added the where statement to see if the associated value was equal to 2. If so, we identified the player as either XanderBogarts or Derek Jeter.