Dictionaries - FUNDAMENTALS - Understanding Swift Programming: Swift 2 (2015)

Understanding Swift Programming: Swift 2 (2015)

PART 1: FUNDAMENTALS

10. Dictionaries

A dictionary in Swift stores pairs of keys and values in an unordered fashion. For example, a dictionary might store names of airports ("San Francisco"), each stored and retrieved by a key serving as an identifier that consists of the standard airport code ("SFO"). Every key in a dictionary must be unique.

All values must be of the same type, and all keys must be of the same type. The type for the values does not have to be the same as that for the keys.

A Swift dictionary that is a variable can be modified; if it is a constant it cannot be modified. This is analogous to the iOS API dictionaries used with Objective-C, where NSMutableDictionary can be modified and NSDictionary cannot be modified.

A dictionary may be initialized with a dictionary literal, as follows:

var airports = ["SFO": "San Francisco", "OAK": "Oakland"]

The dictionary literal consists of one or more key-value pairs, with each key and value separated by a colon. The key-value pairs themselves are separated by a comma, and the entire set of key-value pairs is contained within square brackets.

The example above relies on inferring the type for the key and the value for the dictionary from the initializing information. The following has the same effect, but makes explicit the types:

var airports: [String: String] = ["SFO": "San Francisco", "OAK": "Oakland"]

A Swift dictionary that is a constant is initialized in just the same way as above, except that let is used instead of var to declare that it is a constant:

let airportsConstant = ["SFO": "San Francisco", "OAK": "Oakland"]

or:

let airportsConstant:[String: String] = ["SFO": "San Francisco","OAK": "Oakland"]

Retrieving a Value from a Dictionary: The Optionals Problem

Once data has been entered in a dictionary, it can be retrieved by providing a key for the desired value. If we, for example, want to retrieve the name of a city given the airport code, we can do so by:

var airports = ["SFO": "San Francisco", "OAK": "Oakland"]

var nameOfCity = airports["OAK"]

print (nameOfCity)

// Prints: Oakland

There is one problem, however. It's possible, when retrieving a value from a dictionary, to get the value nil instead. This occurs when an attempt to retrieve a key-value pair is made but the key that is provided does not match a key in the dictionary.

Because of this possibility, the compiler will infer nameOfCity to be an optional type, (string optional, or String?). Before that value can be used, it must be unwrapped.

The recommended way to unwrap the optional is to use optional binding, as follows:

if let nameOfCity = nameOfCity {

print("The unwrapped value is \(nameOfCity)")

}

else {

print("There is no value")

}

// Prints: The unwrapped value is Oakland

For more details see the two chapters on Optionals, Chapter 9 and Chapter 26.

Operations on Dictionaries

To add a new key-value pair to the dictionary, we can do the following:

var airports = ["SFO": "San Francisco", "OAK": "Oakland"]

airports["SJO"] = "San Jose"

This would result in the dictionary containing the following key-value pairs:

"SFO": "San Francisco"

"OAK": "Oakland"

"SJO": "San Jose"

Note that these pairs are in no particular order.

If we decide that we want to change the value in a key-value pair, we can do that with the same syntax:

var airports = ["SFO": "San Francisco", "OAK": "Oakland"]

airports["SFO"] = "San Francisco International"

This would result in the following key-value pairs:

"SFO": "San Francisco International"

"OAK": "Oakland",

Again, these pairs are in no particular order.

If we decide that we no longer want the key value pair SJO: San Jose to be in the dictionary, we can remove it as follows:

var airports = ["SFO": "San Francisco", "OAK": "Oakland", "SJO": "San Jose"]

airports["SJO"] = nil

This would result in the following:

"SFO": "San Francisco"

"OAK": "Oakland"]

And again, these pairs are in no particular order.

This removes the entire key-value pair from the dictionary; it does not set the value associated with "SJO" to nil. (If you now try to access the value using the "SJO" key, you will get nil back, but not because "SJO" has a value stored of nil, but because "SJO" is unknown as a key.)

The number of key-value pairs in a dictionary can be determined by accessing the count property of the dictionary:

var numberOfItems = airports.count

The isEmpty property (a Bool) will tell you whether the dictionary has no elements (has a count of 0):

if(airports.isEmpty) {

print("The airports dictionary has no values in it")

}

else {

print("The airports dictionary has at least one value")

}

You can create an empty dictionary in the following ways:

var airports: [String: String] = Dictionary()

var airports: [String: String] = [:]

You can replace a value in a dictionary with the following and have the old value returned so that it can be assigned to a variable:

var airports = ["SFO": "San Francisco", "OAK": "Oakland"]

var oldValue = airports.updateValue("San Francisco International", forKey: "SFO")

print(oldValue) // Prints: Optional("San Francisco")

This not only replaces the value "San Francisco" with "San Francisco International", but assigns the old value to the variable oldValue.

The method RemoveValueForKey() works similarly, in that as part of deleting a key and value from the dictionary it will return the old value.

The method removeAll() will remove all of the key-value pairs in a dictionary.

Dictionaries also have a keys property and a values property, which contain a collection of all of the keys or all of the values in the dictionary, respectively.

These have a type of Swift.LazyBidirectionalCollection, and to look at them you can use a for-in loop to iterate through them. To look at the values:

var airports = ["SFO": "San Francisco", "OAK": "Oakland"]

for value in airports.values {

print(value)

}

This will print:

San Francisco

Oakland

(Not necessarily in this order)

To look at the keys:

var airports = ["SFO": "San Francisco", "OAK": "Oakland"]

for key in airports.keys {

print(key)

}

This will print:

SFO

OAK

(Again, not necessarily in this order)

Dictionaries, like arrays, are value types. (They are implemented as structures). That this means is that if you make a copy of a dictionary, and change an element, it will not change the original. Thus:

var airports = ["SFO":"San Francisco", "OAK":"Oakland"]

var airports2 = airports

airports2["SFO"] = nil

print (airports) // Prints: [SFO: San Francisco, OAK: Oakland]

print (airports2) // Prints: [OAK: Oakland]

In the Objective-C version of dictionaries, doing the same thing would delete SFO from both airports and airports2, since dictionaries in Objective-C (NSMutableDictionary) are reference types, and copying a dictionary is only copying the reference to it, not the actual dictionary.

We have already seen how to look at all of the keys, or all of the values, of a dictionary. We can also use a for-in loop to look at both the key and the value:

var airports = ["SFO": "San Francisco", "OAK": "Oakland", "LAX": "Los Angeles"]

for (name, airportCode) in airports {

print(name)

print(airportCode)

}

This will print:

San Francisco

SFO

Oakland

OAK

Los Angeles

LAX

(And again, not necessarily in this order)

Hands-On Exercises

Go to the following web address with a Macintosh or Windows PC to do the Hands-On Exercises.

For Chapter 10 exercises, go to

understandingswiftprogramming.com/10