What is a Dictionary

Dictionaries and arrays make up the collection types of Swift. Collections are exactly what they say: a collection of values. The difference between an array and a dictionary is how you get the values in and out of the collection. Arrays, as we covered elsewhere, are ordered collections. They have an index which gives their exact place in the collection. I know with an array a value by its position in the array. I know what is before and after my current position by changing the index from its current position. Arrays must use positive integers in sequence to work this magic.
Dictionaries are unordered collections. No one knows which value comes next, and we really don’t care. The power of a dictionary is we have a key, and the key can pretty much be anything we like that’s a unique value. One of the most common keys will be strings, but integers and doubles can certainly be used. Dictionaries look up values by the key.
While in an array we pair an index with a value, we pair a key with a value in a dictionary. In Swift, all keys are the same type and all values are the same type in a given dictionary. The value can be of any type, even other objects or collection types. We call the key, then a value returns. Often we find a syntax of key:value
for dictionary pairs.
In this lesson for most of these examples, we’ll use a playground. Set up a new playground named SwiftDictionary.
How to Declare a Dictionary
The simplest declaration is to let implicit typing do all the work. We declare a variable or constant and list the key:value pairs in square brackets.
let cookies = ["Chocolate Chip":0.25,"Oatmeal":0.26]
This would create a unmutable dictionary. We can refer to it, but can not change any value. For a mutable dictionary, we could use this:
var mutableCookies = [ "Macadamia Nut":0.06, "Coconut":0.20, "Macaron":0.55 ]
Dictionaries can have entries on one line or multiple lines. To help with readability in a long list of values, you can list them on multiple lines like this:
var gelato = [ "Coconut":0.25, "Pistachio":0.26, "Stracciatella":0.02, "Chocolate":0.03, "Peanut Butter":0.01, "Bubble Gum":0.01 ]
You can explicitly type your of key:value pairs. The preferred way to do this is:
let piePrice:[String:Double] = [ "Apple":3.99, "Raspberry":3.35 ]
We specify the types in the key value pair surrounded by square brackets. Here we have a string for the key, and a double for the value. There is another declaration which is available but frowned on by Apple.
let piPrice:Dictionary<String,Double> = [ "Apple Mac":1200.00, "Raspberry Pi":45.00 ]
This code is identical in function to the previous example but writes out the word Dictionary
and uses the <
and >
characters around the key value pair types.
There are times you need an empty dictionary. In those cases, call the dictionary initializer, which for a dictionary is the key:value pair enclosed in square brackets:
var pieToppings = [String:Double]()
Alternatively, though frowned upon by Apple, you can do this too:
var pizzaSizes = Dictionary<Int, String>()
How to Access a Dictionary
If I wanted to know the price of two slices of pie, the basic syntax is the same for an array, but instead of an index in the square brackets we place the key. We’ve already declared pie prices per slice. How much is an apple pie slice? Like an array’s index, you place the key on brackets:
print(piePrice["Apple"])
How much for a gram of coconut gelato?
print(gelato["Coconut"])
The code piePrice["Apple"]
prints the price of a slice of apple pie and the gelato returns the price per gram.
Optional(3.99) Optional(0.25)
You’ll notice the values returned are optionals. Returned dictionary values are always optionals. At any given time, I do not know if there is a certain type of pie or not in my dictionary. If I tried piePrice["Coconut Cream"]
I need some way to tell me that there is no coconut cream pies in my dictionary. Type
print(piePrice["Coconut Cream"])
The playground print nil
to the console. Swift makes the return value of a dictionary an optional value, so it will return nil
if there is no such entry.
If your dictionary is a constant, you know exactly what’s there. In that case, we can use a forced unwrap to calculate the price for 100 grams of Coconut gelato:
print(gelato["Coconut"]! * 100.0)
If you have a dictionary in a variable, where you may add or delete elements from the dictionary, make sure you check for nil
. For two slices of apple pie, we could write this:
let slices = 2.0 if piePrice["Apple"] != nil { let extPrice = piePrice["Apple"]! * slices print("\(slices) slices of Apple Pie is \(extPrice)") }
or more compactly with optional chaining
if let price = piePrice["Apple"]{ let extPrice = price * slices print("\(slices) slices of Apple Pie is \(extPrice)") }
If you are not familiar with optionals, refer to the Ten Points for Using Optionals post to get yourself up to speed.
Of course, keys are not always literal. We can use a variable or constant. Here we have code to include a constant for the gelato type.:
let scoopSize = 100.0 //grams let gelatoType = "Pistachio" if let price = gelato[gelatoType] { let extPrice = price * scoopSize print("\(scoopSize) grams of " + gelatoType + " gelato is $\(extPrice)") }
Change a Dictionary
Suppose we have a dictionary of prices for pizza by a unit area like a square inch, square cm or such. Add this dictionary:
var toppings = ["Pepperoni":0.25, "Sausage":0.26, "Onions":0.02, "Green Peppers":0.03, "Cheese":0.01 ]
Our cost of Sausage and Cheese increases, and we need to change prices in our dictionary. We can change the value with assignment:
toppings["Sausage"] = 0.29 print(toppings["Sausage"])
Assignment uses subscript syntax and simply changes the value. We can also do this:
toppings.updateValue(0.2, forKey: "Cheese") toppings["Cheese"]
Line 2 uses the updateValue(value:,forKey:)
which does the same as line 1 but the function returns a value. You can write line 2 like this as well:
let oldTopping = toppings.updateValue(0.02,forKey: "Cheese") if oldTopping == nil{ print("Key not found") }
The method returns the value as an optional before we changed it. If there is no key, updateValue(value:,forKey:)
returns nil
. There is a check to tell the developer that this was not in the dictionary. Unlike an array, a key not found is not a fatal error. Instead it adds an element to the dictionary. If we change a value that isn’t there, such as:
let anotherTopping = toppings.updateValue(0.15,forKey: "Gorgonzola") toppings["BBQ Chicken"] = 0.24
Swift will add the new entries for Gorgonzola and BBQ Chicken to the dictionary. There are times that we may not want to add a new element, but give an error or do something else. Using updateValue(value:,forKey:)
we can check for nil
. If nil
, we execute code to delete the new value and do whatever we need to for an unknown key.
To delete a dictionary entry, you can set its value to nil
:
gelato["Bubble Gum"] = nil print(gelato)
You can also use the removeValue(forKey:)
method
let deletedTopping = gelato.removeValue(forKey:"Peanut Butter") print(deletedTopping) print(gelato)
The method removeValue(forKey:)
deletes the element. Like updateValue(value:,forKey:)
the method returns the value deleted. When deleting an element that does not exist, the method tells you by returning nil
, and the subscript syntax does nothing. If you need to know there was no key present, use the method version.
Iterating Dictionaries
Sometimes we need to list or change everything in a dictionary. You can use the for..in
loop to do that. If you need to print a list of all entries and their values in the toppings
dictionary, create a tuple with identifier names:
for (myKey,myValue) in toppings { print("\(myKey) \t \(myValue)") }
We don’t have optionals involved this time, since all values are existing values. If we need to iterate through just keys or values we can do that using the .keys
or .values
.
To list all the keys, we could do this:
for myKey in toppings.keys{ print ("Key = \(myKey)") }
The list that prints may not be in the same order as the list we entered. Dictionaries are unordered collections, and will be near random in order.
We can also do the same thing with the values, but we generally don’t for a really good reason.
for myValue in toppings.values{ print ("Value = \(myValue)") }
The iterator myValue
is a constant inside of the loop. You cannot change this value. For example, if we need to make a 10% increase in all prices for our toppings we can’t write:
for myValue in toppings.values{ myValue = myValue * 1.10 }
The compiler would tell us:
Cannot assign to value: 'myValue' is a 'let' constant
.
We cannot use the values in the for..in
loop to do this. If we want to change values in the loop, we iterate over the keys. Do the calculation directly to the dictionary value based on the iterated key.
//make a 10% price increase for myKey in toppings.keys{ toppings[myKey] = toppings[myKey]! * 1.10 }
Sometimes we need an array of the keys or values, since a particular API, such as UITableViewController
, requires an array. We can use the .keys
or .values
to create an array:
var valuesArray = [Double](toppings.values) let keyArray = [String](toppings.keys)
Passing a Dictionary as a Parameter
Sometimes you will need to pass a lot of values of the same type in a method through a parameter. Instead of making a dozen parameters, you can store their values in a dictionary.
func myFunction(dictionary:[String:Double],key:String)-> Double?{ return dictionary[key] } myFunction(gelato,key: "Coconut")
While this really does nothing special, it make a good prototype function. You can see how to declare a dictionary in a function. Declare the dictionary like you would in an explicit let
or var
statement. Note here I made the return value Double?
to let the optional value through.
Here’s One more example returning an extended price.
func extendedPrice( dict:[String:Double], key:String, units:Double ) -> Double?{ guard let price = dictionary[key] else {return nil} return price * units }
This function has three parameters: the dictionary of the form [String:Double]
, the key and some units to multiply. Try these:
print( extendedPrice(dict: toppings, key: "Pepperoni", units: 22.0)) print(extendedPrice(dict: gelato, key: "Chocolate", units: 150.0)) print(extendedPrice(dict: piPrice, key: "Android", units: 2.0))
Using different dictionaries, you get results like this, including nil
for the not found Android case
Optional(6.0500000000000007) Option
AnyObject and Dictionaries
Strongly typed dictionaries are the Swift Standard. the value is always the same type. Yet there are some dictionary situations that need multiple types. In some situations, we need something like a struct or class that stores several properties, but it is so temporary we don’t want to declare the class or struct.This is very common in Objective-C written API’s. Consider this function:
func dessertAssembly(cookie: String, iceCream:String) -> [String:String]?{ let iceCreamServing = 200.0 var returnDictionary = [String:String]() returnDictionary["Dessert"] = cookie + " with " + iceCream guard let cookiePrice = mutableCookies[cookie] else {return nil} guard let gelatoPrice = gelato[iceCream] else {return nil} returnDictionary["Price"] = cookiePrice + gelatoPrice * iceCreamServing //Double not String return returnDictionary }
We’d like to return for the key Dessert a String
and for Price a Double
. Swift forces us to return for both keys a string and will give us an error when we assign a Double
to our dictionary value.
Binary operator '+' cannot be applied to two 'Double' operands
The compiler expects a string here and thinks +
is string concatenation, not addition. It complains you can’t concatenate cookiePrice
and gelatoPrice * iceCreamServing
since they are doubles.
We could convert the price calculation to a string. We can also do something else: use a dictionary of type [String:AnyObject]
. AnyObject
allows assignment of any object. If we change the code to this, it compiles without errors:
func dessertAssembly(cookie: String, iceCream:String) -> [String:AnyObject]?{ let iceCreamServing = 200.0 var returnDictionary = [String:AnyObject]() returnDictionary["Cookie"] = cookie + " with " + iceCream guard let cookiePrice = mutableCookies[cookie] else {return nil} guard let gelatoPrice = gelato[iceCream] else {return nil} returnDictionary["Price"] = cookiePrice + gelatoPrice * iceCreamServing //double not a string return returnDictionary }
Try this:
let myDessert = dessertAssembly( cookie: "Chocolate Chip", iceCream: "Coconut") print(myDessert?["Price"])
and we get the price.
As a precaution, many developers downcast to the correct type instead of AnyObject
, so they would use
print(myDessert?["Price"] as! Double)
In this example that would need unwrapping the options first, but that my not be a problem in other cases.
Working with TableViews and Dictionaries
As TableViewControllers use IndexPath
, they are best used with arrays, not dictionaries. What if you want to use a dictionary with table views? The best way is to create an array of keys, and then use that array. In the code that defined the dictionary, add a computed property to get the keys, in this case for gelato
var keyList:[String] { get{ return [String](gelato.keys) } }
In tableview: cellForRowAt indexpath:
for your table, you access your key from keyList
, and get the value using that key.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let row = indexPath.row //get the array index from the index path var cell = tableView.dequeueReusableCell(withIdentifier:"tableCell") //make the cell if cell == nil { cell = UITableViewCell(style: .value1, reuseIdentifier: "cell") } let myRowKey = keyList[row] //the dictionary key cell?.textLabel?.text = myRowKey let myRowData = gelato[myRowKey] //the dictionary value cell?.detailTextLabel?.text = String(format: "%6.3f",myRowData!) return cell! }
I used the dictionary starting in the highlighted line 7. Dictionaries are non-ordered, and for an ordered object like a table view, I need something to correspond in an ordered fashion to my table view’s indexPath
. I used the computed property typelist
, which is an array of keys. This is a good way to get a dictionary ordered when needed. Make an array of keys and iterate over the array pointing to the dictionary. In the highlighted Line 9 I take the key and get the price.

We’ve kept the value’s type to something simple. You can declare a value as a class instead. In my pizza ordering system, I could have a class describing a pizza order, and use a key to hold the order number. Often people use sequential numbers for the order number. If so, an array might work there. Some systems hide data in the order number, like the date, time or server ID.
Hopefully this was a helpful introduction to Dictionaries in Swift.
The Whole Code
Here is a file of the playground for this lesson. While typing in the code above is very useful you can use this to experiment with dictionaries.
//: Playground - noun: a place where people can play import UIKit let cookies = ["Chocolate Chip":0.25,"Oatmeal":0.26] var mutableCookies = [ "Macadamia Nut":0.06, "Coconut":0.20, "Macaron":0.55 ] var gelato = [ "Coconut":0.025, "Pistachio":0.026, "Stracciatella":0.002, "Chocolate":0.003, "Peanut Butter":0.001, "Bubble Gum":0.001 ] let piePrice:[String:Double] = [ "Apple":3.99, "Raspberry":3.35 ] let piPrice:Dictionary<String,Double> = [ "Apple Mac":1200.00, "Raspberry Pi":45.00 ] var pieToppings = [String:Double]() var pizzaSizes = Dictionary<Int, String>() print(piePrice["Apple"]) print(gelato["Coconut"]) print(piePrice["Coconut Cream"]) print(gelato["Coconut"]! * 100.0) let slices = 2.0 if piePrice["Apple"] != nil { let extPrice = piePrice["Apple"]! * slices print("\(slices) slices of Apple Pie is \(extPrice)") } if let price = piePrice["Apple"]{ let extPrice = price * slices print("\(slices) slices of Apple Pie is \(extPrice)") } let scoopSize = 100.0 //grams let gelatoType = "Pistachio" if let price = gelato[gelatoType] { let extPrice = price * scoopSize print("\(scoopSize) grams of " + gelatoType + " gelato is $\(extPrice)") } //: # Changing a Dictionary var toppings = ["Pepperoni":0.25, "Sausage":0.26, "Onions":0.02, "Green Peppers":0.03, "Cheese":0.01 ] toppings["Sausage"] = 0.29 print(toppings["Sausage"]) toppings.updateValue(0.2, forKey: "Cheese") print(toppings["Cheese"]) let oldTopping = toppings.updateValue(0.02,forKey: "Cheese") if oldTopping == nil{ print("Key not found") } let anotherTopping = toppings.updateValue(0.15,forKey: "Gorgonzola") toppings["BBQ Chicken"] = 0.24 gelato["Bubble Gum"] = nil print(gelato) let deletedTopping = gelato.removeValue(forKey:"Peanut Butter") print(deletedTopping) print(gelato) for (myKey,myValue) in toppings { print("\(myKey) \t \(myValue)") } for myKey in toppings.keys{ print ("Key = \(myKey)") } //: Commented out since myValue is a constant /*for myValue in toppings.values{ myValue = myValue * 1.10 } */ //: Correct way to itereate over key values //make a 10% price increase for myKey in toppings.keys{ toppings[myKey] = toppings[myKey]! * 1.10 } //: Making an array of keys and values var valuesArray = [Double](toppings.values) let keyArray = [String](toppings.keys) //: #Parameters func extendedPrice(dictionary:[String:Double],key:String,units:Double) -> Double?{ guard let price = dictionary[key] else {return nil} return price * units } print( extendedPrice(dictionary: toppings, key: "Pepperoni", units: 22.0)) print(extendedPrice(dictionary: gelato, key: "Chocolate", units: 150.0)) print(extendedPrice(dictionary: piPrice, key: "Android", units: 2.0)) //: AnyObject in action //: This does not compile /* func dessertAssembly(cookie: String, iceCream:String) -> [String:String]?{ let iceCreamServing = 200.0 var returnDictionary = [String:String]() returnDictionary["Cookie"] = cookie + " with " + iceCream guard let cookiePrice = cookies[cookie] else {return nil} guard let gelatoPrice = gelato[iceCream] else {return nil} returnDictionary["Price"] = cookiePrice + gelatoPrice * iceCreamServing //double not a string return returnDictionary } */ //this does compile func dessertAssembly(cookie: String, iceCream:String) -> [String:AnyObject]?{ let iceCreamServing = 200.0 var returnDictionary = [String:AnyObject]() returnDictionary["Cookie"] = cookie + " with " + iceCream guard let cookiePrice = cookies[cookie] else {return nil} guard let gelatoPrice = gelato[iceCream] else {return nil} returnDictionary["Price"] = cookiePrice + gelatoPrice * iceCreamServing //double not a string return returnDictionary } let myDessert = dessertAssembly( cookie: "Chocolate Chip", iceCream: "Coconut") print(myDessert?["Price"]) //: Table View Controller var keyList:[String] { get{ return [String](gelato.keys) } } //comment out on IBM Sandbox. class TableViewController:UITableViewController{ override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return gelato.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let row = indexPath.row //get the array index from the index path var cell = tableView.dequeueReusableCell(withIdentifier:"tableCell") //make the cell if cell == nil { cell = UITableViewCell(style: .value1, reuseIdentifier: "cell") } let myRowKey = keyList[row] //the dictionary key cell?.textLabel?.text = myRowKey let myRowData = gelato[myRowKey] //the dictionary value cell?.detailTextLabel?.text = String(format: "%6.3f",myRowData!) return cell! } } import PlaygroundSupport let vc = TableViewController() PlaygroundPage.current.liveView = vc PlaygroundPage.current.needsIndefiniteExecution = true
Leave a Reply