kodeco.com Forums

Video Tutorial: Beginning Swift 2 Part 8: Dictionaries

Learn how to use dictionaries in Swift 2 to map one thing to another.


This is a companion discussion topic for the original entry at http://www.raywenderlich.com/115543/video-tutorial-beginning-swift-2-part-8-dictionaries

Small bug in the playgrounds associated with this post.

In the calculateTip(), there is an error which causes it to return the total bill amount as the tip amount when an incorrect rating is given. This should instead return ‘0’.
Line 36 in Demo starter, 26 in Demo finished, and 23 in challenge starter.

  func calculateTip() -> Double {
    guard let tipPercentage = tipPercentages[rating] else {
      return totalBill //should be 0
    }

    return totalBill * tipPercentage
  }

Thanks for the concise explanations. I found the videos helpful even as an experienced programmer (mostly python, some Java).

1 Like

Thanks @scone, nice catch! I’ve updated the playground files.

Hi Greg,

I was very confused with the tutorial solution. why is thistotal used here? the entire set of code below is so confusing…can u please provide a simpler solution for absolute beginners please.

// Key = restaurant name, value = total
var totalsByRestaurant: [String: Double] = [:]

// loop through all restaurant bills
for bill in restaurantBills {
** if let thisTotal = totalsByRestaurant[bill.restaurant.name] {**
** // there’s already a total for this restaurant!**
** totalsByRestaurant[bill.restaurant.name] = thisTotal + bill.totalBill + bill.calculateTip()**
** } else {**
** // this is the first time seeing this restaurant in the dictionary**
** totalsByRestaurant[bill.restaurant.name] = bill.totalBill + bill.calculateTip()**
** }**
}

// show the results
for (restaurantName, total) in totalsByRestaurant {
** print(“The total spent at (restaurantName): $(total)”)**
}

I was also struggling with this and was quite confused but I think I understand now. This is my hypothesis:

If you instantiate a constant in an if statement as well as setting the value, you are also evaluating it as Boolean.

It evaluates as a boolean True if the constant is set to a non nil value
It evaluates as a boolean False if set to a nil value

So in the solution it is a non nil value when the dictionary entry already exists (and then you add the value to the existing total).

But it is nil if the dictionary entry does not exist, so the dictionary entry is be created with the value of that bill

Adding a bit more to the comments:

if let thisTotal = totalsByRestaurant[bill.restaurant.name] {
// there’s already a total for this restaurant!
// Evaluated to True because totalsByRestaurant[bill.restaurant.name] already exists
// so the new billl is added to thisTotal
totalsByRestaurant[bill.restaurant.name] = thisTotal + bill.totalBill + bill.calculateTip()
} else {
// this is the first time seeing this restaurant in the dictionary
// evaluated to false as totalsByRestaurant[bill.restaurant.name] does not exist for this restaurant so is nil
// so the restaurant entry in the dictionary is actually created here
totalsByRestaurant[bill.restaurant.name] = bill.totalBill + bill.calculateTip()
}

if let thisTotal = totalsByRestaurant[bill.restaurant.name]

This is suggesting that if totalsByRestaurant[bill.restaurant.name] isn’t nil, than let thisTotal be a temporary constant equal to totalsByRestaurant[bill.restaurant.name]. Is that right? If yes, does that mean that thisTotal is temporarily a String?

If yes, then how in the next line does it somehow mutate into a double?

totalsByRestaurant[bill.restaurant.name] = thisTotal + bill.totalBill + bill.calculateTip()

Ah wait. It doesn’t equal the String, but the Key (which happens to be a String). And the Key unlocks the Value which is a Double.

(Is that right? Please tell me that’s right.)

And then, bill is running the array of restaurantBills, sequentially, and each time it hits the same restaurant name, it runs the “if” part of the statement and the first time it hits a different restaurant name, it runs the “else” part of the statement.

(Is that right? Please tell me that’s right.)

And then for some odd reason, the syntax to initialize (is that the right word?) [ : ] is ( , ) in a for-in loop.

(Is that right? Please tell me that’s right.)

Alright. So here’s my question. (At long last.) There is no way in hell I could have generated that language on my own accord. If my understanding above is correct, I get it conceptually. But is that enough to move forward? Or do you recommend I repeat the tutorials until I can generate the answer to the challenge without peeking?

So maybe this will help clarify. An empty dictionary called totalsByRestaurant is defined, with a key : value pair of String : Double. A restaurant’s name will be the String key and the Double will be the total bill amount for that restaurant name key.

We loop through the array of restaurant bills. The statement
if let thisTotal = totalsByRestaurant[bill.restaurant.name]
is saying, if the expression evaluates to anything other than nil, assign the value associated with that key to thisTotal. So now, thisTotal has a value and it is used inside the curly braces in the calculation of the new value to update the dictionary.

The first time through the for loop, the else clause will be executed because this is the first iteration and the restaurant name has not appeared yet in the dictionary. Subsequently, the constant thisTotal is not used in the else clause in the statement totalsByRestaurant[bill.restaurant.name] = bill.totalBill + bill.calculateTip(), which initializes the first entry in the dictionary for that specific key.