Chapter 11: Dictionaries

Swift Programming from Scratch

The book is updated for Swift 3 and 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

?You can buy Training app  +  the PDF and ePub versions of the book!

    cover final small     +     cover final small
Buy now!

Chapter 11: Dictionaries

 

achievement11Small@2x

 

A dictionary is an unordered collection that stores multiple values of the same type. Each value from the dictionary is asociated with a unique key. All the keys have the same type.

The type of a dictionary is determined by the type of the keys and the type of the values. A dictionary of type[String:Int] has keys of type String and values of type Int.

Declare Dictionaries

To declare a dictionary you can use the square brackets syntax([KeyType:ValueType]).

var dictionary: [String:Int]

You can initialize a dictionary with a dictionary literal. A dictionary literal is a list of key-value pairs, separated by commas, surrounded by a pair of square brackets. A key-value pair is a combination of a key and a value separate by a colon(:).

[key:value, key:value, ...]

var dictionary: [String:Int] = [
    "one" : 1,
    "two" : 2,
    "three" : 3
]

Keep in mind that you can create empty dictionary using the empty dictionary literal ([:]).

var emptyDictionary: [Int:Int] = [:]

Getting values

You can access specific elements from a dictionary using the subscript syntax. To do this pass the key of the value you want to retrieve within square brackets immediately after the name of the dictionary. Because it’s possible not to have a value associated with the provided key the subscript will return an optional value of the value type.

To unwrap the value returned by the subscript you can do one of two things: use optional binding or force the value if you know for sure it exists.

var stringsAsInts: [String:Int] = [
    "zero" : 0,
    "one" : 1,
    "two" : 2,
    "three" : 3,
    "four" : 4,
    "five" : 5,
    "six" : 6,
    "seven" : 7,
    "eight" : 8,
    "nine" : 9
]

stringsAsInts["zero"] // Optional(0)
stringsAsInts["three"] // Optional(3)
stringsAsInts["ten"] // nil

// Unwaraping the optional using optional binding
if let twoAsInt = stringsAsInts["two"] {
    print(twoAsInt) // 2
}

// Unwaraping the optional using the forced value operator (!)
stringsAsInts["one"]! // 1

To get all the values from a dictionary you can use the for-in syntax. It’s similar to the array for in syntax with the exception that instead of getting only the value in each step you also get the key associated with that value inside of a tuple.

var userInfo: [String: String] = [
    "first_name" : "Andrei",
    "last_name" : "Puni",
    "job_title" : "Mad scientist"
]

for (key, value) in userInfo {
    print("\(key): \(value)")
}

To get the number of elements (key-value pairs) in a dictionary you can use the count property.

print(userInfo.count) // 3

Updating values

The simplest way to add a value to a dictionary is by using the subscript syntax:

var stringsAsInts: [String:Int] = [
    "zero" : 0,
    "one" : 1,
    "two" : 2
]

stringsAsInts["three"] = 3

Using the subscript syntax you can change a the value associated with a key:

stringsAsInts["three"] = 10

You can use the updateValue(forKey:) method to update the value associated with a key, if there was no value for that key it will be added. The method will return the old value wrapped in an optional or nil if there was no value before.

var stringsAsInts: [String:Int] = [
    "zero" : 0,
    "one" : 1,
    "two" : 2
]

stringsAsInts.updateValue(3, forKey: "three") // nil 
stringsAsInts.updateValue(10, forKey: "three") // Optional(3)

To remove a value from the dictionary you can use the subscript syntax to set the value to nil, or theremoveValueForKey() method.

stringsAsInts["three"] = nil

stringsAsInts.removeValueForKey("three")

Type Inference

Thanks to Swift’s type inference, you don’t have to declare the type of a dictionary if you initialize it with something other than an empty dictionary literal([:]).

// powersOfTwo will have the type [Int:Int]
var powersOfTwo = [
    1 : 2,
    2 : 4,
    3 : 8,
    4 : 16
] 

// userInfo will have the type [String:String]
var userInfo = [
    "first_name" : "Silviu",
    "last_name" : "Pop",
    "job_title" : "evil genius"
]

Copy Behavior

Swift’s dictionaries are value types. This means that dictionaries are copied when they are assigned to a new constant or variable, or when they are passed to a function or method.

var stringsAsInts: [String:Int] = [
    "zero" : 0,
    "one" : 1,
    "two" : 2
]

var justACopy = stringsAsInts

justACopy["zero"] = 100

print(stringsAsInts) // [zero: 0, one: 1, two: 2]
print(justACopy) // [zero: 100, one: 1, two: 2]

Keep in mind that this is not true for Objective-C dictionaries (NSDictionary and NSMutableDictionary).

Mutability

If you create a dictionary and assign it to a variable, the collection that is created will be mutable. This means that you can change (or mutate) the collection after it is created by adding, removing, or changing items in the collection. Conversely, if you assign a dictionary to a constant, that array or dictionary is immutable, and its size and contents cannot be changed. In other words if you want to be able to change a dictionary declare it using the var keyword, and if you don’t want to be able to change it use the let keyword.

var stringsAsInts: [String:Int] = [
    "zero" : 0,
    "one" : 1,
    "two" : 2
]

stringsAsInts["three"] = 3 // [zero: 0, one: 1, two: 2, three: 3]
stringsAsInts["zero"] = nil // [one: 1, two: 2, three: 3]

let powersOfTwo = [
    1 : 2,
    2 : 4,
    3 : 8,
    4 : 16
]

// this will give a runtime error because powersOfTwo is immutable
powersOfTwo[5] = 32 

powersOfTwo.removeValueForKey(1) // this will give a similar error

11.1 Encode

You are given a dictionary code of type [String:String] which has values for all lowercase letters. The codedictionary represents a way to encode a message. For example if code["a"] = "z" and code["b"] = "x" the encoded version if "ababa" will be "zxzxz". You are also given a message which contains only lowercase letters and spaces. Use the code dictionary to encode the message and print it.

Example 1

Input:

var code = [
    "a" : "b",
    "b" : "c",
    "c" : "d",
    "d" : "e",
    "e" : "f",
    "f" : "g",
    "g" : "h",
    "h" : "i",
    "i" : "j",
    "j" : "k",
    "k" : "l",
    "l" : "m",
    "m" : "n",
    "n" : "o",
    "o" : "p",
    "p" : "q",
    "q" : "r",
    "r" : "s",
    "s" : "t",
    "t" : "u",
    "u" : "v",
    "v" : "w",
    "w" : "x",
    "x" : "y",
    "y" : "z",
    "z" : "a"
]

var message = "hello world"

Output:

ifmmp xpsme

[collapse]
Example 2

Input:

var code = [
    "a" : "b",
    "b" : "c",
    "c" : "d",
    "d" : "e",
    "e" : "f",
    "f" : "g",
    "g" : "h",
    "h" : "i",
    "i" : "j",
    "j" : "k",
    "k" : "l",
    "l" : "m",
    "m" : "n",
    "n" : "o",
    "o" : "p",
    "p" : "q",
    "q" : "r",
    "r" : "s",
    "s" : "t",
    "t" : "u",
    "u" : "v",
    "v" : "w",
    "w" : "x",
    "x" : "y",
    "y" : "z",
    "z" : "a"
]

var message = "wow this problem is hard"

Output:

xpx uijt qspcmfn jt ibse

[collapse]
Hint 1

If a character doesn’t have a corresponding encoded character leave it unchanged.

[collapse]
Hint 2

Build the encoded message step by step by getting the corresponding encoded character from the dictionary.

[collapse]
Solution

var code = [
    "a" : "b",
    "b" : "c",
    "c" : "d",
    "d" : "e",
    "e" : "f",
    "f" : "g",
    "g" : "h",
    "h" : "i",
    "i" : "j",
    "j" : "k",
    "k" : "l",
    "l" : "m",
    "m" : "n",
    "n" : "o",
    "o" : "p",
    "p" : "q",
    "q" : "r",
    "r" : "s",
    "s" : "t",
    "t" : "u",
    "u" : "v",
    "v" : "w",
    "w" : "x",
    "x" : "y",
    "y" : "z",
    "z" : "a"
]

var message = "hello world"

var encodedMessage = ""

for char in message.characters {
    var character = "\(char)"

    if let encodedChar = code[character] {
        // letter
        encodedMessage += encodedChar
    } else {
        // space
        encodedMessage += character
    }
}

print(encodedMessage)

[collapse]

11.2 Decode

You are given a dictionary code of type [String:String] which has values for all lowercase letters. The codedictionary represents a way to encode a message. For example if code["a"] = "z" and code["b"] = "x" the encoded version if "ababa" will be "zxzxz". You are also given a encodedMessage which contains only lowercase letters and spaces. Use the code dictionary to decode the message and print it.

Example

Input:

var code = [
    "a" : "b",
    "b" : "c",
    "c" : "d",
    "d" : "e",
    "e" : "f",
    "f" : "g",
    "g" : "h",
    "h" : "i",
    "i" : "j",
    "j" : "k",
    "k" : "l",
    "l" : "m",
    "m" : "n",
    "n" : "o",
    "o" : "p",
    "p" : "q",
    "q" : "r",
    "r" : "s",
    "s" : "t",
    "t" : "u",
    "u" : "v",
    "v" : "w",
    "w" : "x",
    "x" : "y",
    "y" : "z",
    "z" : "a"
]

var encodedMessage = "uijt nfttbhf jt ibse up sfbe"

Output:

this message is hard to read

[collapse]
Hint

You’ll have to invert the code dictionary. Create a new dictionary for this.

[collapse]
Solution

var code = [
    "a" : "b",
    "b" : "c",
    "c" : "d",
    "d" : "e",
    "e" : "f",
    "f" : "g",
    "g" : "h",
    "h" : "i",
    "i" : "j",
    "j" : "k",
    "k" : "l",
    "l" : "m",
    "m" : "n",
    "n" : "o",
    "o" : "p",
    "p" : "q",
    "q" : "r",
    "r" : "s",
    "s" : "t",
    "t" : "u",
    "u" : "v",
    "v" : "w",
    "w" : "x",
    "x" : "y",
    "y" : "z",
    "z" : "a"
]

var encodedMessage = "uijt nfttbhf jt ibse up sfbe"

var decoder: [String:String] = [:]

// reverse the code
for (key, value) in code {
    decoder[value] = key
}

var decodedMessage = ""

for char in encodedMessage.characters {
    var character = "\(char)"

    if let encodedChar = decoder[character] {
        // letter
        decodedMessage += encodedChar
    } else {
        // space
        decodedMessage += character
    }
}

print(decodedMessage)

[collapse]

11.3 Names

You are given an array of dictionaries. Each dictionary in the array contains exactly 2 keys “firstName” and “lastName”. Create an array of strings called firstNames that contains only the values for “firstName” from each dictionary.

This content is restricted to buyers of Online Exercise Platform.

Example

Input:

var people: [[String:String]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton"
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie"
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera"
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno"
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen"
    ]
]

Expected values:

firstNames = ["Calvin","Garry","Leah","Sonja","Noel"]

[collapse]
Solution

var people: [[String:String]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton"
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie"
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera"
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno"
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen"
    ]
]

var firstNames: [String] = []

for person in people {
    if let firstName = person["firstName"] {
        print(firstName)
        firstNames.append(firstName)
    }
}

[collapse]
Hint

Keep in mind that persons is an array of dictionaries, you’ll have to process this array to get the required data.

[collapse]

11.4 Full names

You are given an array of dictionaries. Each dictionary in the array contains exactly 2 keys “firstName” and “lastName”. Create an array of strings called fullNames that contains the values for “firstName” and “lastName” from the dictionary separated by a space.

This content is restricted to buyers of Online Exercise Platform.

Example

Input:

var people: [[String:String]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton"
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie"
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera"
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno"
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen"
    ]
]

Expected values:

fullNames = ["Calvin Newton","Garry Mckenzie","Leah Rivera",
    "Sonja Moreno","Noel Bowen"]

[collapse]
Solution 1

var people: [[String:String]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton"
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie"
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera"
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno"
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen"
    ]
]

var fullNames: [String] = []

for person in people {
    if let firstName = person["firstName"] {
        if let lastName = person["lastName"] {
            let fullName = "\(firstName) \(lastName)"
            fullNames.append(fullName)
        }
    }
}

[collapse]
Solution 2

var people: [[String:String]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton"
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie"
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera"
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno"
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen"
    ]
]

var fullNames: [String] = []

for person in people {
    var fullName = " "
    for (key, value) in person {
        if key == "lastName" {
            fullName += value
        } else {
            fullName = value + fullName
        }
    }
    fullNames += [fullName]
}

print(fullNames)

[collapse]

11.5 Best score

You are given an array of dictionaries. Each dictionary in the array describes the score of a person. Find the person with the best score and print his full name.

This content is restricted to buyers of Online Exercise Platform.

Example

Input:

var people: [[String:Any]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton",
        "score": 13
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie",
        "score": 23
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera",
        "score": 10
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno",
        "score": 3
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen",
        "score": 16
    ]
]

Output:

Garry Mckenzie

[collapse]
Hint

Keep track of the person with the best score that you’ve encountered.

[collapse]
Solution

var people: [[String:Any]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton",
        "score": 13
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie",
        "score": 23
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera",
        "score": 10
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno",
        "score": 3
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen",
        "score": 16
    ]
]

var topPerson = people[0]
var bestScore = topPerson["score"] as! Int

for person in people {
    if let score = person["score"] as? Int {
        if bestScore < score  {
            bestScore = score
            topPerson = person
        }
    }
}

if let first = topPerson["firstName"] as? String,
   let second = topPerson["lastName"] as? String  {
    print("\(first) \(second)")
}

[collapse]

11.6 Leaderboard

You are given an array of dictionaries. Each dictionary in the array describes the score of a person. Print the leaderboard in the following format:

1. full name - score
2. ...
...

This content is restricted to buyers of Online Exercise Platform.

Example

Input:

var people: [[String:Any]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton",
        "score": 13
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie",
        "score": 23
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera",
        "score": 10
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno",
        "score": 3
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen",
        "score": 16
    ]
]

Output:

1. Garry Mckenzie - 23
2. Noel Bowen - 16
3. Calvin Newton - 13
4. Leah Rivera - 10
5. Sonja Moreno - 3

[collapse]
Hint

Sort the list of people using a function that compares two dictionaries.

[collapse]
Solution

var people: [[String:Any]] = [
    [
        "firstName": "Calvin",
        "lastName": "Newton",
        "score": 13
    ],
    [
        "firstName": "Garry",
        "lastName": "Mckenzie",
        "score": 23
    ],
    [
        "firstName": "Leah",
        "lastName": "Rivera",
        "score": 10
    ],
    [
        "firstName": "Sonja",
        "lastName": "Moreno",
        "score": 3
    ],
    [
        "firstName": "Noel",
        "lastName": "Bowen",
        "score": 16
    ]
]

func compareScores(_ first: [String:Any], second: [String:Any]) -> Bool {
    if let a = first["score"] as? Int {
        if let b = second["score"] as? Int {
            return a > b
        }
    }
    return false
}

people.sort(by: compareScores)

for (index, person) in people.enumerated() {
    if let firstName = person["firstName"] as? String {
        if let lastName = person["lastName"] as? String  {
            if let score = person["score"] as? Int {
                print("\(index + 1). \(firstName) \(lastName) - \(score)")
            }
        }
    }
}

[collapse]

11.7 Frequency

You are given an array of integers. Find out the frequency of each one.
The frequency of a number is the number of times it appears in the array.
Print the numbers in ascending order followed by their frequency.

This content is restricted to buyers of Online Exercise Platform.

Example

Input:

var numbers = [1, 2, 3, 2, 3, 5, 2, 1, 3, 4, 2, 2, 2]

Output:

1 2
2 6
3 3
4 1
5 1

[collapse]
Hint 1

Use a dictionary to keep track of the frequency.

[collapse]
Hint 2

Keep track of all the unique numbers of the array

[collapse]
Solution

var numbers = [1, 2, 3, 2, 3, 5, 2, 1, 3, 4, 2, 2, 2]

var frequency: [Int:Int] = [:]
var uniqueNumbers: [Int] = []

for number in numbers {
    if let oldFr = frequency[number] {
        frequency[number] = oldFr + 1
    } else {
        uniqueNumbers.append(number)
        frequency[number] = 1
    }
}

uniqueNumbers.sort(by: <)

for number in uniqueNumbers {
    print("\(number) \(frequency[number]!)")
}

[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

?You can buy Training app  +  the PDF and ePub versions of the book!

    cover final small     +     cover final small
Buy now!

 

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

 

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

  18 comments for “Chapter 11: Dictionaries

  1. kevin
    February 19, 2015 at 2:38 am

    The “a” is a typo?

    var dictionary: [String:Int] = [a
    “one” : 1,
    “two” : 2,
    “three” : 3
    ]

  2. Gonzo
    February 23, 2015 at 2:06 am

    Thank you for the really helpful exercises! Could you please explain the as? Int component of 11.5? Thanks!

  3. SimNico
    March 19, 2015 at 3:37 pm

    11.6 – Leaderboard Solution

    The compareScores function definition does not compile, I think you meant :

    func compareScores(first: [String:Any], second: [String:Any]) -> Bool {
    // code
    }

  4. johnnydash
    May 27, 2015 at 12:02 pm

    hi can we create dictionary and arrays as optionals?

    sth like var Dict:[String:CGFloat]?

  5. antyK
    June 18, 2015 at 11:43 pm

    In the last solution:

    uniqueNumbers.sort(<)

    for number in uniqueNumbers {
    println("\(number) \(frequency[number]!)")
    }
    "

    Will unique.Numbers.sort(<) actually affect the variable?

    Shouldn't it rather be inside the for-loop? Like so:
    for number in uniqueNumbers.sort(<) {
    … }

    • June 19, 2015 at 8:06 am

      uniqueNumbers.sort(<) will affect the variable. If you want to create a new sorted array without affecting the variable you can use the "sorted" method.Note that in Swift 2.0 this behaviour will change. "sort" will work like "sorted" (producing a new array instead of affecting the variable). There's a new method "sortInPlace" that affects the variable without producing a new array.

  6. June 25, 2015 at 12:24 pm

    What about ‘Class’ Syntax..

  7. matt
    January 1, 2016 at 9:50 pm

    >// this will give a runtime error because powersOfTwo is immutable
    >powersOfTwo[5] = 32

    This is a compile time error. Not runtime?

    • January 2, 2016 at 1:23 pm

      yes. powersOfTwo is a constant and cannot be mutated.

  8. matt
    January 3, 2016 at 5:20 am

    My solution for 11.6 leaderBoard:

    https://gist.github.com/mattyoung/49ebd775801d877d9bb7

    Using an Array of [String:Any] as data structure require a lot of type casting from Any to the actual type. Change the array to hold a struct of specific type climate the type casting.

    https://gist.github.com/mattyoung/b7cae1c39d7d0f21fae6

  9. Byrne
    February 26, 2016 at 6:55 am

    A dictionary may be copied but if it’s values are reference types they will still be referenced, not copied. I found this out with [String: [SCNNode]] dictionaries after losing some nodes from the original as well as the so-called-copy.

  10. Karuppasamy pandian
    November 8, 2016 at 7:45 am

    very nice tutorial thank you

  11. taylor
    December 24, 2016 at 8:25 pm

    Hi, you have errors/bugs throughout the Dictionaries tutorial. Problems 11.4 to 11.6 will not execute. It’s saying “internal error”

    On problem 11.7 it’s saying I got the correct answer, but when comparing my results with the expected results, I clearly got it wrong.

    • taylor
      December 24, 2016 at 8:29 pm

      Actually, I might have gotten it right on 11.7. My answers are correct, just not in the same order. Does this mean that when you add keys and values to a dictionary it stores them in random order?

Leave a Reply

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

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