Chapter 10: Tuples & Enums

Swift Programming from Scratch

The Swift Sandbox is integrated, making the exercises interactive. Read more about the book here.

  1. First Steps
  2. Conditionals
  3. Types
  4. Loops
  5. Strings
  6. Arrays
  7. Functions
  8. Recursion
  9. Closures
  10. Tuples & Enums
  11. Dictionaries

Chapter 10: Tuples & Enums

achievement10@2x

 

Tuples

A tuple is a group of zero or more values represented as one value.

For example ("John", "Smith") holds the first and last name of a person. You can access the inner values using the dot(.) notation followed by the index of the value:

var person = ("John", "Smith")

var firstName = person.0 // John
var lastName = person.1 // Smith
Named elements

You can name the elements from a tuple and use those names to refer to them. An element name is an identifier followed by a colon(:).

var person = (firstName: "John", lastName: "Smith")

var firstName = person.firstName // John
var lastName = person.lastName // Smith
Creating a tuple

You can declare a tuple like any other variable or constant. To initialize it you will need a another tuple or a tuple literal. A tuple literal is a list of values separated by commas between a pair of parentheses. You can use the dot notation to change the values from a tuple if it’s declared as a variable.

var point = (0, 0)

point.0 = 10
point.1 = 15

point // (10, 15)

Note: Tuple are value types. When you initialize a variable tuple with another one it will actually create a copy.

var origin = (x: 0, y: 0)

var point = origin
point.x = 3
point.y = 5

print(origin) // (0, 0)
print(point) // (3, 5)
Types

The type of a tuple is determined by the values it has. So ("tuple", 1, true) will be of type (String, Int, Bool). You can have tuples with any combination of zero or more types.

If the tuple has only one element then the type of that tuple is the type of the element. (Int) is the same as Int. This has a strange implication: in swift every variable or constant is a tuple.

var number = 123

print(number) // 123
print(number.0) // 123
print(number.0.0) // 123
print(number.0.0.0) // 123
print(number.0.0.0.0.0.0.0) // 123
Empty tuple

() is the empty tuple – it has no elements. It also represents the Void type.

Decomposing Tuples
var person = (firstName: "John", lastName: "Smith")

var (firstName, lastName) = person

var (onlyFirstName, _) = person
var (_, onlyLastName) = person

Note: the _ means “I don’t care about that value”

Multiple assignment

You can use tuples to initialize more than one variable on a single line:

Instead of:

var a = 1
var b = 2
var c = 3

you can write:

var (a, b, c) = (1, 2, 3)

Instead of:

a = 1
b = 2
c = 3

you can write:

(a, b, c) = (1, 2, 3)

And yes! One line swap:

(a, b) = (b, a)
Returning multiple values

You can return multiple values from a function if you set the result type to a tuple. Here is a simple example of a function that return the quotient and the remainder of the division of a by b.

func divmod(_ a: Int, _ b:Int) -> (Int, Int) {
    return (a / b, a % b)
}

divmod(7, 3) // (2, 1)
divmod(5, 2) // (2, 1)
divmod(12, 4) // (3, 0)

Or the named version:

func divmod(_ a: Int, _ b:Int) -> (quotient: Int, remainder: Int) {
    return (a / b, a % b)
}

divmod(7, 3) // (quotient: 2, remainder:1)
divmod(5, 2) // (quotient: 2, remainder:1)
divmod(12, 4) // (quotient: 3, remainder:0)

Enums

An enumeration is a data type consisting of a set of named values, called members.

Defining an enumeration

You can define a new enumeration using the enum keyword followed by it’s name. The member values are introduced using the case keyword.

enum iOSDeviceType {
    case iPhone
    case iPad
    case iWatch
}

var myDevice = iOSDeviceType.iPhone
Dot syntax

If the type of an enumeration is known or can be inferred then you can use the dot syntax for members.

// in this case the type is known
var myDevice: iOSDeviceType = .iPhone 

// in this case the type can be inferred
if myDevice == .iPhone {
    print("I have an iPhone!")
}
Associated Values

Swift enumerations can store associated values of any type, and the value type can be different for each member. For example you might want to store a device model for the iPhone and iPad (like "mini" for the iPad, or "6 Plus"for the iPhone).

enum iOSDeviceType {
    case iPhone(String)
    case iPad(String)
    case iWatch
}

You can get the associated values by using a switch statement:

var myDevice = iOSDeviceType.iPhone("6")

switch myDevice {
case .iPhone(let model):
    print("iPhone \(model)")
case .iPad(let model):
    print("iPad \(model)")
case .iWatch:
    print("iWatch")
default:
    print("not an iOS device")
}

// iPhone 6

Note: Swift does not provide equality operators automatically for enumerations with associated values. You might be tempted to use nested switch statements in order to test equality. Don’t forget the tuple pattern!

var myDevice = iOSDeviceType.iPhone("6")
var six = iOSDeviceType.iPhone("6")
var sixPlus = iOSDeviceType.iPhone("6 Plus")

// testing equlity with == wont work
// myDevice == six
// myDevice == sixPlus

func sameDevice(_ firstDevice: iOSDeviceType, 
        secondDevice: iOSDeviceType) -> Bool {
    switch (firstDevice, secondDevice) {
    case (.iPhone(let a), .iPhone(let b)) where a == b:
        return true
    case (.iPad(let a), .iPad(let b)) where a == b:
        return true
    case (.iWatch, .iWatch):
        return true
    default:
        return false
    }
}

print(sameDevice(myDevice, six)) // true
print(sameDevice(myDevice, sixPlus)) // false
print(sameDevice(myDevice, .iWatch)) // false
Raw Values

Enums can have a raw value (a primitive type – Int, String, Character, etc.) associated with each member. The raw value will be of the same type for all members and the value for each member must be unique. When integers are use they autoincrement is a value is not defined for a member.

enum Direction: Int {
    case up = 1
    case down // will have the raw value 2
    case left // will have the raw value 3
    case right // will have the raw value 4
}

You can use raw values to create a enumeration value.

var direction = Direction(rawValue: 4) // .Right

print(direction) // Optional((Enum Value))

Note: Because not all raw values have an associated member value the raw value initializer is a failable initializer. The type of direction is Direction? not Direction.

10.1 Game

You are working on a game in which your character is exploring a grid-like map. You get the original location of the character and the steps he will take.
A step .Up will increase the x coordinate by 1. A step .Down will decrease the x coordinate by 1. A step .Right will increase the y coordinate by 1. A step .Left will decrease the y coordinate by 1.
Print the final location of the character after all the steps have been taken.

Example 1

Input:

var location = (x: 0, y: 0)

var steps: [Direction] = [.up, .up, .left, .down, .left]

Output:

(-2, 1)

[collapse]
Example 2

Input:

var location = (x: 0, y: 0)

var steps: [Direction] = [.up, .up, .left, .down, .left, .down, .down, 
    .right, .right, .down, .right]

Output:

(1, -2)

[collapse]
Example 3

Input:

var location = (x: 5, y: 2)

var steps: [Direction] = [.up, .right, .up, .right, .up, 
    .right, .down, .right]

Output:

(9, 4)

[collapse]
Hint 1

Use a switch statement.

[collapse]
Hint 2

Modify the location tuple based on what case you’re handling in theswitch statement.

[collapse]
Solution

enum Direction {
    case up
    case down
    case left
    case right
}

var location = (x: 0, y: 0)

var steps: [Direction] = [.up, .up, .left, .down, .left]

for step in steps {
    switch step {
    case .up:
        location.y += 1
    case .down:
        location.y -= 1
    case .right:
        location.x += 1
    case .left:
        location.x -= 1
    default:
        break
    }
}

print(location)

[collapse]

10.2 Min Max

Write a function named minmax that takes two integers and returns both the minimum and the maximum values inside a tuple.

Example 1

Function call:

minmax(2, 3)

Function output:

(2, 3)

[collapse]
Example 2

Function call:

minmax(5, 1)

Function output:

(1, 5)

[collapse]
Example 3

Function call:

minmax(3, 3)

Function output:

(3, 3)

[collapse]
Function Definition

func minmax(_ a: Int, _ b: Int) -> (Int, Int)

[collapse]
Hint 1

A single comparison is enough to determine both the minimum and the maximum value.

[collapse]
Solution

func minmax(_ a: Int, _ b: Int) -> (Int, Int) {
    if a < b {
        return (a, b)
    } else {
        return (b, a)
    }
}

[collapse]

10.3 Rock, Paper, Scissors

1) Define an enumeration named HandShape with three members: .rock, .paper, .scissors.
2) Define an enumeration named MatchResult with three members: .win, .draw, .lose.
3) write a function called match that takes two hand shapes and returns a match result. It should return the outcome for the first player (the one with the first hand shape).

This content is restricted to buyers of Online Exercise Platform.

Function Definition

func match(_ first: HandShape, _ second: HandShape) -> MatchResult

[collapse]
Example 1

Function call:

match(.rock, .scissors)

Function output:

.win

[collapse]
Example 2

Function call:

match(.rock, .paper)

Function output:

.lose

[collapse]
Example 3

Function call:

match(.scissors, .scissors)

Function output:

.draw

[collapse]
Hint 1

Handle the case when the hands result in a draw first.

[collapse]
Hint 2

Determine if a win has occurred.

[collapse]
Solution

enum HandShape {
    case rock
    case paper
    case scissors
}

enum MatchResult {
    case win
    case draw
    case lose
}

func match(_ first: HandShape, _ second: HandShape) -> MatchResult {
    if first == second {
        return .draw
    }

    if first == .rock && second == .scissors {
        return .win
    }

    if first == .paper && second == .rock {
        return .win
    }

    if first == .scissors && second == .paper {
        return .win
    }

    return .lose
}

[collapse]

10.4 Fractions

You are given 2 tuples of a, b type (Int,Int) representing fractions. The first value in the tuple represents the numerator, the second value represents the denominator. Create a new tuple sum of type (Int,Int) that holds the sum of the fractions.

This content is restricted to buyers of Online Exercise Platform.

Example 1

Input:

var a = (5,8)
var b = (17,9)

Expected Value:

sum = (181, 72)

[collapse]
Example 2

Input:

var a = (34,3)
var b = (11,2)

Expected Value:

sum = (101, 6)

[collapse]
Hint

To add 2 fractions together you have to get them to a common denominator.

[collapse]
Solution

var a = (5,8)
var b = (17,9)

let numerator = a.0 * b.1 + b.0 * a.1
let denominator = a.1 * b.1
var sum = (numerator, denominator)

[collapse]

10.5 Money

You are given the CoinType enumeration which describes different coin values and moneyArray which has tuples(ammount, coinType). Print the total value of the coins in the array.

This content is restricted to buyers of Online Exercise Platform.

Example 1

Input:

var moneyArray:[(Int,CoinType)] = [(10,.penny),
    (15,.nickle),
    (3,.quarter),
    (20,.penny),
    (3,.dime),
    (7,.quarter)]

Output:

385

[collapse]
Example 2

Input:

var moneyArray:[(Int,CoinType)] = [
    (2,.penny),
    (3,.quarter)
]

Output:

77

[collapse]
Example 3

Input:

var moneyArray:[(Int,CoinType)] = [
    (5, .dime),
    (2, .quarter),
    (1, .nickle)
]

Output:

105

[collapse]
Hint

Remember that .rawValue gets the numeric value associated with an enum value.

[collapse]
Solution

enum CoinType: Int {
    case penny = 1
    case nickle = 5
    case dime = 10
    case quarter = 25
}


var moneyArray:[(Int,CoinType)] = [(10,.penny),
                                   (15,.nickle),
                                   (3,.quarter),
                                   (20,.penny),
                                   (3,.dime),
                                   (7,.quarter)]

var totalMoney = 0

for (amount, coinType) in moneyArray {
    totalMoney += amount * coinType.rawValue
}

print(totalMoney)

[collapse]

10.6 Counting Strings

You are given an array of strings stored in the variable strings. Create a new array named countedStringscontaining values of type (String,Int). Each tuple contains a string from the strings array followed by an integer indicating how many times it appears in the strings array. Each string should only appear once in thecountedStrings array.

This content is restricted to buyers of Online Exercise Platform.

Example 1

Input:

var strings = ["tuples", "are", "awesome", "tuples", "are", "cool", 
    "tuples", "tuples", "tuples", "shades"]

Expected Value:

countedStrings = [
    "tuples" : 5,
    "are" : 2,
    "awesome" : 1,
    "cool" : 1,
    "shades" : 1
]

[collapse]
Example 2

Input:

var strings = ["hello", "world", "hello", "swift", "hello", "tuples"]

Expected Value:

countedStrings = [
    "hello" : 3,
    "world" : 1,
    "swift" : 1,
    "tuples" : 1
]

[collapse]
Hint

Keep in mind that you can’t change a tuple when iterating an array using the for in syntax. You’ll have to iterate using an index.

[collapse]
Solution

var strings = ["tuples", "are", "awesome", "tuples", "are", "cool", 
    "tuples", "tuples", "tuples", "shades"]

var countedStrings: [(String,Int)] = []

for string in strings {
    var alreadyExists = false

    for i in 0..<countedStrings.count {
        if (countedStrings[i].0 == string) {
            countedStrings[i].1 += 1
            alreadyExists = true
        }
    }
    if alreadyExists == false {
        let tuple = (string,1)
        countedStrings.append((string,1))
    }
}

[collapse]

Swift Programming from Scratch

Read more about the book here.

  1. First Steps
  2. Conditionals
  3. Types
  4. Loops
  5. Strings
  6. Arrays
  7. Functions
  8. Recursion
  9. Closures
  10. Tuples & Enums
  11. Dictionaries

Feel free to ask any questions in the comments bellow. :)

 

 

1 Star2 Stars3 Stars4 Stars5 Stars (4 votes, average: 4.50 out of 5)
Loading...

  6 comments for “Chapter 10: Tuples & Enums

  1. matt
    January 1, 2016 at 9:30 pm

    My solution. Don’t look if you don’t want to see.

    Using a dictionary:

    var strings = [“tuples”, “are”, “awesome”, “tuples”, “are”, “cool”, “tuples”, “tuples”, “tuples”, “shades”, “awesome”]

    var countedStrings: [(String, Int)] = []

    var counters: [String: Int] = [:]

    for s in strings {
    if let count = counters[s] {
    counters[s] = count + 1
    } else {
    counters[s] = 1
    }
    }

    for (key, value) in counters {
    countedStrings.append((key, value))
    }

    print(countedStrings)

  2. Duncan Rowland
    November 22, 2016 at 8:51 am

    Makes sense to use reduce for the coins…
    print(moneyArray.reduce(0, {$0+$1.0*$1.1.rawValue}))

  3. Michael
    November 27, 2016 at 1:19 pm

    Exercise 10.3 — seems to have a problem. I had a solution very similar to the posted solution, but it says the function is not defined correctly. If I paste it in another Xcode playground and use the examples, I get the intended results. If I copy the solution code into the exercise, I get the same error message. I’m going to assume there is something wrong with this one on the platform and move on (but that kills me not to check the box!) :)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe
We send about one email per week with our latest tutorials and updates
Never display this again :)