There is an updated edition of this book available! View Latest Edition

# 20 Pattern Matching Written by Ben Morrow

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

In this chapter, you’ll learn about proper golf attire: How to pair a striped shirt with plaid shorts:

No, just playing! This is not your grandfather’s pattern matching.

Actually, you’ve already seen pattern matching in action. In Chapter 4, “Advanced Control Flow”, you used a `switch` statement to match numbers and strings in different cases. That’s a simple example, but there’s a lot more to explore on the topic.

You’re going to dive deep into the underlying mechanisms and understand more about how the Swift compiler interprets the code you type.

Swift is a multi-paradigm language that lets you build full-featured, production ready, object-oriented software. The designers of Swift borrowed some tricks from more functional style languages like Haskell and Erlang.

Pattern matching is a staple of those functional languages. It saves you from having to type much longer and less readable statements to evaluate conditions.

Suppose you have a coordinate with x-, y-, and z- axis values:

``````let coordinate = (x: 1, y: 0, z: 0)
``````

Both of these code snippets will achieve the same result:

``````// 1
if (coordinate.y == 0) && (coordinate.z == 0) {
print("along the x-axis")
}

// 2
if case (_, 0, 0) = coordinate {
print("along the x-axis")
}
``````

The first option digs into the internals of a tuple and has a lengthy equatable comparison. It also uses the logical `&&` operator to make sure both conditions are `true`.

The second option, using pattern matching, is concise and readable.

The following sections will show you how — and when — to use patterns in your code.

## Introducing patterns

Patterns provide rules to match values. You can use patterns in `switch` cases, as well as in `if`, `while`, `guard`, and `for` statements. You can also use patterns in variable and constant declarations.

Believe it or not, you’ve already seen another good example of patterns with that `coordinate` tuple declaration. You construct a tuple by separating values with commas between parentheses, like `(x, y, z)`. The compiler will understand that pattern is referring to a tuple of 3 values: x, y and z. Tuples have the structure of a composite value.

Single values also have a structure. The number `42` is a single value and by its very nature is identifiable.

A pattern defines the structure of a value. Pattern matching lets you check values against each other.

Note: The structure of a value doesn’t refer to the `struct` type. They are different concepts, even though they use the same word. Could be a symptom of the paucity of language!

## Basic pattern matching

In this section, you’ll see some common uses for pattern matching.

### If and guard

Throughout the book so far, you’ve used plain old `if` and `guard` statements. You can transform them into pattern matching statements by using a `case` condition. The example below shows how you use an `if` statement with a `case` condition:

``````func process(point: (x: Int, y: Int, z: Int)) -> String {
if case (0, 0, 0) = point {
return "At origin"
}
return "Not at origin"
}

let point = (x: 0, y: 0, z: 0)
let status = process(point: point) // At origin
``````
``````func process(point: (x: Int, y: Int, z: Int)) -> String {
guard case (0, 0, 0) = point else {
return "Not at origin"
}
// guaranteed point is at the origin
return "At origin"
}
``````

### Switch

If you care to match multiple patterns, the `switch` statement is your best friend.

``````func process(point: (x: Int, y: Int, z: Int)) -> String {
// 1
let closeRange = -2...2
let midRange = -5...5
// 2
switch point {
case (0, 0, 0):
return "At origin"
case (closeRange, closeRange, closeRange):
return "Very close to origin"
case (midRange, midRange, midRange):
return "Nearby origin"
default:
return "Not near origin"
}
}

let point = (x: 15, y: 5, z: 3)
let status = process(point: point) // Not near origin
``````

### Mini exercise

Given the population of a group of people, write a `switch` statement that prints out a comment for different ranges of group sizes: single, a few, several and many.

### for

A `for` loop churns through a collection of elements. Pattern matching can act as a filter:

``````let groupSizes = [1, 5, 4, 6, 2, 1, 3]
for case 1 in groupSizes {
print("Found an individual") // 2 times
}
``````

## Patterns

Now that you’ve seen some basic examples of pattern matching, let’s talk about the patterns on which you can match.

### Wildcard pattern

Revisit the example you saw at the beginning of this chapter, where you wanted to check if a value was on the x-axis, for the `(x, y, z)` tuple coordinate:

``````if case (_, 0, 0) = coordinate {
// x can be any value. y and z must be exactly 0.
print("On the x-axis") // Printed!
}
``````

### Value-binding pattern

The value-binding pattern sounds more sophisticated than it turns out to be in practice. You simply use `var` or `let` to declare a variable or a constant while matching a pattern.

``````if case (let x, 0, 0) = coordinate {
print("On the x-axis at \(x)") // Printed: 1
}
``````
``````if case let (x, y, 0) = coordinate {
print("On the x-y plane at (\(x), \(y))") // Printed: 1, 0
}
``````

### Identifier pattern

The identifier pattern is even more straightforward than the value-binding pattern. The identifier pattern is the constant or variable name itself; in the example above, that’s the `x` in the pattern. You’re telling the compiler, “When you find a value of `(something, 0, 0)`, assign the `something` to `x`.”

### Tuple pattern

You’ve already been using another bonus pattern — did you recognize it? The tuple isn’t just a series of comma-separated values between parentheses: it’s actually comma-separated patterns. In the example tuple pattern, `(something, 0, 0)`, the interior patterns are (identifier, expression, expression).

### Enumeration case pattern

In Chapter 15, “Enumerations”, you saw how you could match the member values of an enumeration:

``````enum Direction {
case north, south, east, west
}

if case .north = heading {
print("Don’t forget your jacket") // Printed!
}
``````
``````enum Organism {
case plant
case animal(legs: Int)
}

let pet = Organism.animal(legs: 4)

switch pet {
case .animal(let legs):
print("Potentially cuddly with \(legs) legs") // Printed: 4
default:
print("No chance for cuddles")
}
``````

### Mini exercise

In Chapter 15, “Enumerations” you learned that an optional is an enumeration under the hood. An optional is either `.some(value)` or `.none`. You just learned how to extract associated values from optionals. Given the following array of optionals, print the names that are not `nil` with a `for` loop:

``````let names: [String?] =
["Michelle", nil, "Brandon", "Christine", nil, "David"]
``````

### Optional pattern

Speaking of optionals, there is also an optional pattern. The optional pattern consists of an identifier pattern followed immediately by a question mark. You can use this pattern in the same places you would use enumeration case patterns.

``````for case let name? in names {
print(name) // 4 times
}
``````

### “Is” type-casting pattern

By using the `is` operator in a case condition, you check if an instance is of a particular type. An example of when to use this is parsing through a JSON export. In case you’re not familiar, JSON is basically an array full of all different types, which you can write as `[Any]` in Swift. Web APIs and website developers make use of JSON a lot.

``````let response: [Any] = [15, "George", 2.0]

for element in response {
switch element {
case is String:
print("Found a string") // 1 time
default:
print("Found something else") // 2 times
}
}
``````

### “As” type-casting pattern

The `as` operator combines the `is` type casting pattern with the value-binding pattern. Extending the example above, you could write a case like this:

``````for element in response {
switch element {
case let text as String:
print("Found a string: \(text)") // 1 time
default:
print("Found something else") // 2 times
}
}
``````

You’ve blazed through all the above patterns! What you’ve learned so far in this chapter will carry you quite far as a developer. In the upcoming section, you’ll learn some modifier tricks that enable you to consolidate your code even further.

### Qualifying with where

You can specify a `where` condition to further filter a match by checking a unary condition in-line. In Chapter 4, “Advanced Control Flow”, you saw an example like this:

``````for number in 1...9 {
switch number {
case let x where x % 2 == 0:
print("even") // 4 times
default:
print("odd") // 5 times
}
}
``````
``````enum LevelStatus {
case complete
case inProgress(percent: Double)
case notStarted
}

let levels: [LevelStatus] =
[.complete, .inProgress(percent: 0.9), .notStarted]

for level in levels {
switch level {
case .inProgress(let percent) where percent > 0.8 :
print("Almost there!")
case .inProgress(let percent) where percent > 0.5 :
print("Halfway there!")
case .inProgress(let percent) where percent > 0.2 :
default:
break
}
}
``````

### Chaining with commas

Another thing you learned in Chapter 4, “Advanced Control Flow”, was how to match multiple patterns in a single-case condition. Here’s an example similar to what you saw previously:

``````func timeOfDayDescription(hour: Int) -> String {
switch hour {
case 0, 1, 2, 3, 4, 5:
return "Early morning"
case 6, 7, 8, 9, 10, 11:
return "Morning"
case 12, 13, 14, 15, 16:
return "Afternoon"
case 17, 18, 19:
return "Evening"
case 20, 21, 22, 23:
return "Late evening"
default:
return "INVALID HOUR!"
}
}
let timeOfDay = timeOfDayDescription(hour: 12) // Afternoon
``````
``````if case .animal(let legs) = pet, case 2...4 = legs {
print("potentially cuddly") // Printed!
} else {
print("no chance for cuddles")
}
``````
``````enum Number {
case integerValue(Int)
case doubleValue(Double)
case booleanValue(Bool)
}

let a = 5
let b = 6
let c: Number? = .integerValue(7)
let d: Number? = .integerValue(8)

if a != b {
if let c = c {
if let d = d {
if case .integerValue(let cValue) = c {
if case .integerValue(let dValue) = d {
if dValue > cValue {
print("a and b are different") // Printed!
print("d is greater than c") // Printed!
print("sum: \(a + b + cValue + dValue)") // 26
}
}
}
}
}
}
``````
``````if a != b,
let c = c,
let d = d,
case .integerValue(let cValue) = c,
case .integerValue(let dValue) = d,
dValue > cValue {
print("a and b are different") // Printed!
print("d is greater than c") // Printed!
print("sum: \(a + b + cValue + dValue)") // Printed: 26
}
``````

### Custom tuple

In this chapter, you saw how a tuple pattern could match a three-dimensional coordinate, `(x, y, z)`. You can create a just-in-time tuple expression at the moment you’re ready to match it.

``````let name = "Bob"
let age = 23

if case ("Bob", 23) = (name, age) {
print("Found the right Bob!") // Printed!
}
``````
``````var username: String?

case (nil, nil):
}
``````

### Fun with wildcards

One fun way to use the wildcard pattern is within the definition of a `for` loop:

``````for _ in 1...3 {
print("hi") // 3 times
}
``````

#### Validate that an optional exists

``````let user: String? = "Bob"
guard let _ = user else {
print("There is no user.")
fatalError()
}
print("User exists, but identity not needed.") // Printed!
``````

In this code, you check to make sure `user` has a value. You use the underscore to indicate that, right now, you don’t care what value it contains.

``````guard user != nil else {
print("There is no user.")
fatalError()
}
``````

#### Organize an if-else-if

In app development, views are defined by a rectangle. Here’s a simplified version:

``````struct Rectangle {
let width: Int
let height: Int
let background: String
}

let view = Rectangle(width: 15, height: 60, background: "Green")
switch view {
case _ where view.height < 50:
print("Shorter than 50 units")
case _ where view.width > 20:
print("Over 50 tall, & over 20 wide")
case _ where view.background == "Green":
print("Over 50 tall, at most 20 wide, & green") // Printed!
default:
print("This view can’t be described by this example")
}
``````

## Programming exercises

As you develop confidence with Swift, you may find yourself applying for a job where you’d use Swift at work. Hiring interviews have some classic questions like the Fibonacci and FizzBuzz algorithms. Pattern matching can come in handy for both of these challenges.

#### Fibonacci

In the Fibonacci sequence, every element is the sum of the two preceding elements. The sequence starts with 0, 1, 1, 2, 3, 5, 8 …

``````func fibonacci(position: Int) -> Int {
switch position {
// 1
case let n where n <= 1:
return 0
// 2
case 2:
return 1
// 3
case let n:
return fibonacci(position: n - 1) + fibonacci(position: n - 2)
}
}

let fib15 = fibonacci(position: 15) // 377
``````

#### FizzBuzz

In the FizzBuzz algorithm, your objective is to print the numbers from 1 to 100, except:

``````for i in 1...100 {
// 1
switch (i % 3, i % 5) {
// 2
case (0, 0):
print("FizzBuzz", terminator: " ")
case (0, _):
print("Fizz", terminator: " ")
case (_, 0):
print("Buzz", terminator: " ")
// 3
case (_, _):
print(i, terminator: " ")
}
}
print("")
``````

## Expression pattern

With all the pattern matching skills you’ve developed so far, you’re finally ready to learn what’s underneath the hood. The expression pattern is simple, but oh, so powerful.

``````let matched = (1...10 ~= 5) // true
``````
``````if case 1...10 = 5 {
print("In the range")
}
``````

### Overloading `~=`

You can overload the `~=` operator to provide your own custom expression matching behavior. You’ll implement a pattern match between an array and an integer to check if the integer is an element of the array. A value of `2` should match the pattern `[0, 1, 2, 3]`. With the standard library, you’ll get an error on this code:

``````let list = [0, 1, 2, 3]
let integer = 2

let isInArray = (list ~= integer) // Error!

if case list = integer { // Error!
print("The integer is in the array")
} else {
print("The integer is not in the array")
}
``````
``````let isInList = list.contains(integer) // true
``````
``````// 1
func ~=(pattern: [Int], value: Int) -> Bool {
// 2
for i in pattern {
if i == value {
// 3
return true
}
}
// 4
return false
}
``````
``````let isInArray = (list ~= integer) // true

if case list = integer {
print("The integer is in the array") // Printed!
} else {
print("The integer is not in the array")
}
``````

## Challenges

Before moving on, here are some challenges to test your knowledge of pattern matching. It is best if you try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.

### Challenge 1: Carded

Given this code, write an `if` statement that shows an error if the user is not yet 21 years old:

``````enum FormField {
case firstName(String)
case lastName(String)
case age(Int)
}
let minimumAge = 21
let submittedAge = FormField.age(22)
``````

### Challenge 2: Planets with liquid water

Given this code, find the planets with liquid water using a `for` loop:

``````enum CelestialBody {
case star
case planet(liquidWater: Bool)
case comet
}

let telescopeCensus = [
CelestialBody.star,
.planet(liquidWater: false),
.planet(liquidWater: true),
.planet(liquidWater: true),
.comet
]
``````

### Challenge 3: Find the year

Given this code, find the albums that were released in 1974 with a `for` loop:

``````let queenAlbums = [
("A Night at the Opera", 1974),
("Sheer Heart Attack", 1974),
("Jazz", 1978),
("The Game", 1980)
]
``````

### Challenge 4: Where in the world

Given this code, write a `switch` statement that will print out whether the monument is located in the northern hemisphere, the southern hemisphere, or on the equator.

``````let coordinates = (lat: 192.89483, long: -68.887463)
``````

## Key points

• A pattern represents the structure of a value.  