Dictionary in Swift

The general syntax to create a Swift dictionary is var dict: Dictionary<Key, Value>. This code creates a mutable instance of the Dictionary type called dict. The declarations for what types the dictionary’s keys and values accept are inside the angle brackets (<>), denoted here by Key and Value.

The values stored in a dictionary can be of any type, just like the values in an array. The only type requirement for keys in a Swift Dictionary is that the type must be hashable. The basic concept is that each Key type must provide a mechanism to guarantee that its instances are unique. Swift’s basic types, such as StringIntFloatDouble, and Bool, are all hashable.

Before you begin typing code, let’s take a look at the different ways you can explicitly declare an instance of Dictionary:

    var dict1: Dictionary<String, Int>
    var dict2: [String:Int]

Both options yield the same result: an uninitialized Dictionary whose keys are String instances and whose values are of type Int. The second example uses the dictionary literal syntax ([:]).

As with Swift’s other data types, you can also declare and initialize a dictionary in one line. In that case, you can explicitly declare the types of the keys and values or take advantage of type inference:

    var companyZIPCode: [String:Int] = ["Big Nerd Ranch": 30307]
    var sameCompanyZIPCode = ["Big Nerd Ranch": 30307]

Again, these two options yield the same result: a dictionary initialized with a single key-value pair consisting of a String key, "Big Nerd Ranch", and an Int value, 30307.

It is useful to take advantage of Swift’s type-inference capabilities. Type inference creates code that is more concise but just as expressive. Accordingly, you will stick with type inference in this tutorial.

Time to create your own dictionary. Start with a new macOS playground called Dictionary. Declare a dictionary called movieRatings and use type inference to initialize it with some data.

Listing 10.1  Creating a dictionary

import Cocoa

var str = "Hello, playground"

var movieRatings =                                         
        ["Tron": 4, "WarGames": 5, "Sneakers": 4]

(Since dictionaries are not ordered, the sidebar result may show the key-value pairs in a different order each time your code executes.)

You created a mutable dictionary to hold movie ratings using the Dictionary literal syntax. Its keys are instances of String and represent individual movies. These keys map onto values that are instances of Int that represent the ratings of the movies.

As an aside, just as you can create an array literal with no elements using [], you can create a dictionary with no keys or values using [:]. As with arrays, this syntax omits anything the compiler could use to infer the key and value types, so that information would have to be declared explicitly.

Accessing and Modifying Values

Now that you have a mutable dictionary, how do you work with it? You will want to read from and modify the dictionary. Begin by using count to get some useful information about your dictionary.

Listing 10.2  Using count

var movieRatings =                                         ["Tron": 4, "WarGames":...
        ["Tron": 4, "WarGames": 5, "Sneakers": 4]
movieRatings.count                                         3

Now, read a value from the movieRatings dictionary.

Listing 10.3  Reading a value from the dictionary

var movieRatings =                                         ["Tron": 4, "WarGames":...
        ["Tron": 4, "WarGames": 5, "Sneakers": 4]
movieRatings.count                                         3
let tronRating = movieRatings["Tron"]                      4

The brackets in movieRatings["Tron"] are the subscripting syntax you have seen before. But because dictionaries are not ordered, you do not use an index to find a particular value. Instead, you access values from a dictionary by supplying the key associated with the value you would like to retrieve. In the example above, you supply the key "Tron", so tronRating is set to 4 – the value associated with that key.

Option-click the tronRating instance to get more information (Figure 10.1).

Figure 10.1  Option-clicking tronRating

Option-clicking tronRating

Xcode tells you that its type is Int?, but movieRatings has type [String: Int]. Why the discrepancy? When you subscript a Dictionary instance for a given key, the dictionary will return an optional matching the type of the Dictionary’s values. This is because the Dictionary type needs a way to tell you that the value you asked for is not present. For example, you have not rated Primer yet, so let primerRating = movieRatings["Primer"] would result in primerRating having type Int? and being set to nil.

A dictionary’s keys are constants: They cannot be mutated. The informal contract a dictionary makes is something like “Give me a value, and a key to store it by, and I’ll remember both. Come back with the key later, and I’ll look up its value for you.” If a key were able to mutate, that could break the dictionary’s ability to find its related value later.

But values can be mutated. Modify a value in your dictionary of movie ratings:

Listing 10.4  Modifying a value

...
movieRatings.count                                         3
let tronRating = movieRatings["Tron"]                      4
movieRatings["Sneakers"] = 5                               5
movieRatings                                               ["Sneakers": 5, "WarGam...

As you can see, the value associated with the key "Sneakers" is now 5.

There is another useful way to update values associated with a dictionary’s keys: the updateValue(_:forKey:) method. It takes two arguments: The first, value, takes the new value. The second, forKey, specifies the key whose value you would like to change.

There is one small caveat: updateValue(_:forKey:) returns an optional, because the key may not exist in the dictionary. But that actually makes this method more useful, because it gives you a handle on the last value to which the key mapped, using optional binding. Let’s see this in action.

Listing 10.5  Updating a value

...
movieRatings["Sneakers"] = 5                               5
movieRatings                                               ["Sneakers": 5, "WarGam...
let oldRating: Int? =                                      4
        movieRatings.updateValue(5, forKey: "Tron")
if let lastRating = oldRating, let currentRating =
        movieRatings["Tron"] {
    print("old rating: \(lastRating)")                     "old rating: 4\n"
    print("current rating: \(currentRating)")              "current rating: 5\n"
}

Adding and Removing Values

Now that you have seen how to update a value, let’s look at how you can add or remove key-value pairs. Begin by adding a value.

Listing 10.6  Adding a value

...
if let lastRating = oldRating, let currentRating =
        movieRatings["Tron"] {
    print("old rating: \(lastRating)")                     "old rating: 4\n"
    print("current rating: \(currentRating)")              "current rating: 5\n"
}
movieRatings["Hackers"] = 5                                5

Here, you add a new key-value pair to your dictionary using the syntax movieRatings["Hackers"] = 5. You use the assignment operator to associate a value (in this case, 5) with the new key ("Hackers").

Next, remove the entry for Sneakers.

Listing 10.7  Removing a value

...
if let lastRating = oldRating, let currentRating =
        movieRatings["Tron"] {
    ...
}
movieRatings["Hackers"] = 5                                5
movieRatings.removeValue(forKey: "Sneakers")               5

The method removeValue(forKey:) takes a key as an argument and removes the key-value pair that matches what you provide. Now, movieRatings has no entry for Sneakers.

Additionally, this method returns the value the key was associated with, if the key is found and removed successfully. In the example above, you could have typed let removedRating: Int? = movieRatings.removeValue(forKey: "Sneakers"). Because removeValue(forKey:) returns an optional of the type that was removed, removedRating would be an optional Int. Placing the old value in a variable or constant like this can be handy if you need to do something with the old value.

However, you do not have to assign the method’s return value to anything. If the key is found in the dictionary, then the key-value pair is removed whether or not you assign the old value to a variable.

You can also remove a key-value pair by setting a key’s value to nil.

Listing 10.8  Setting the key’s value to nil

...
if let lastRating = oldRating, let currentRating =
        movieRatings["Tron"] {
    ...
}
movieRatings["Hackers"] = 5                                5
movieRatings.removeValue(forKey: "Sneakers")
movieRatings["Sneakers"] = nil                             nil

The result is essentially the same, but this strategy does not return the removed key’s value.

Written by

XR Developer responsible for end-to-end development of XR solutions spanning multiple domains, by using various XR and WebXR libraries.

Leave a Reply