The Tuple Pattern

Jonathan Lehr

|

5 min read
A woman holding out two fingers during a sunset
let dogInfo: [String: Any] = ["name": "Fido", "breed": "Lab", "age": 5]

for (key, value) in dogInfo {
    print("\(key): \(value)")
}
// Prints
// name: Fido
// breed: Lab
// age: 5


Understanding the Tuple Pattern

let temperature = (72, "Fahrenheit")
print(temperature)   // Prints "(72, "Fahrenheit")"
print(temperature.0) // Prints "72"
print(temperature.1) // Prints "Fahrenheit"
var temperature: (Double, String)

A pattern represents the structure of a single value or a composite value. For example, the structure of a tuple (1, 2) is a comma-separated list of two elements. Because patterns represent the structure of a value rather than any one particular value, you can match them with a variety of values. For instance, the pattern (x, y) matches the tuple (1, 2) and any other two-element tuple. In addition to matching a pattern with a value, you can extract part or all of a composite value and bind each part to a constant or variable name.

var item: (Double, Int)
item  = (19.99, 2)


Binding Individual Elements

let (x, y) = (10, 20)
let x = 10, y = 20
let (x, y) = (0.5, "foo")


Accessing Tuple Elements

let item  = (19.99, 2)

print("price: \(item.0), quantity: \(item.1)")

// Prints "price: 19.99, quantity: 2"
let item = (price: 19.99, quantity: 2)

print("price: \(item.price), quantity: \(item.quantity)")

// Prints "price: 19.99, quantity: 2"
// Defines a computed property with two named elements,
// 'price' and 'quantity', that returns (Double, Int).
//
var defaultItem: (price: Double, quantity: Int) {
    return (19.99, 2)
}
// Defines individual let constants, 'amount' and 'number'.
// Compiler infers their types from the type of 'defaultItem`.
//
let (amount, number) = defaultItem

print("price: \(amount), quantity: \(number)")
// Prints "price: 19.99, quantity: 2"


The Enumeration Case and Value Binding Patterns


Enumeration Case Pattern

An enumeration case pattern matches a case of an existing enumeration type. Enumeration case patterns appear in switch statement case labels and in the case conditions of ifwhileguard, and for-in statements.


Value-Binding Pattern
// An array whose elements are tuples representing price and quantity
let items = [(12.99, 2), (14.95, 3), (19.99, 2)]
for item in items {
    // Binds price value to 'amount'
    // Enters the 'if' statement's body if quantity matches the pattern '2'
    if case (let amount, 2) = item {
        print(amount)
    }
}
// Prints 
// "12.99"
// "19.99"

// Note that the let keyword can be moved outside the tuple
for item in items {
    if case let (amount, 2) = item {
        print(amount)
    }
    ...
let discount1 = 10.0
let discount2 = 14.0

let items = [("Shirt", 44.99), ("Shoes", 89.99), ("Jeans", 64.99)]

for item in items {
    // Applies a $10 discount for shirts and a $14 discount for shoes by
    // pattern matching on the string value of the first element
    switch item {
    case let ("Shirt", p):  print("Shirt: $\(p - discount1)")
    case let ("Shoes", p):  print("Shoes: $\(p - discount2)")
    case let (itemName, p): print("\(itemName): $\(p)")
    }
}
// Prints
// Shirt: $34.99
// Shoes: $75.99
// Jeans: $64.99


Back to the Future

let dogInfo: [String: Any] = ["name": "Fido", "breed": "Lab", "age": 5]
for (key, value) in dogInfo {
    print("\(key): \(value)")
}
// Valid Swift, but `case let` will be inferred by the compiler if omitted.
for case let (key, value) in dogInfo {
    print("\(key): \(value)")
}
mutating func next() -> (key: Key, value: Value)?

As you can see, next() returns an optional, generically typed tuple with named elements. (Note: we’ll explore optional values and the Optional type in detail in a future blog post.) So we now know that the for-in loop could also have been written like so (though generally, the earlier style is preferable):

for item in dogInfo {
    print("\(item.key): \(item.value)")
}
// Prints
// name: Fido
// breed: Lab
// age: 5

Here we simply capture the current tuple in item, and then access item‘s elements by name in the argument to the print() function.


Conclusion

Some of the concepts embodied in Swift may seem unintuitive to those of us coming from primarily object-oriented language backgrounds. A solid understanding of Swift patterns can help a lot with comprehension when reading non-trivial code, and can be indispensible when struggling to figure out how to make use of various features of the language to implement details of the apps we’re writing.

Newsletter Form
  • VA ProdOps and Data Quality Engineering
    VA ProdOps and Data Quality Engineering
    See more: VA ProdOps and Data Quality Engineering
    Small orange arrow symbol
  • Where the future is going: sniffing out all of Apple’s clues
    Where the future is going: sniffing out all of Apple’s clues
    See more: Where the future is going: sniffing out all of Apple’s clues
    Small orange arrow symbol
Abstract graffiti-style artwork with vivid colors and dynamic shapes.
Simple Form

Connect with our team.

Error

An error has occured, please try again.

Try again