Dictionaries
Dictionaries are mutable, unordered collections of key-value associations. Dictionaries may contain a key only once and may contain a value multiple times.
- Dictionary literals start with an opening brace
{
and end with a closing brace}
. - Keys are separated from values by a colon, and key-value associations are separated by commas.
_10// An empty dictionary_10//_10{}_10_10// A dictionary which associates integers with booleans_10//_10{_10 1: true,_10 2: false_10}
Dictionary types
Dictionary types have the form {K: V}
, where K
is the type of the key, and V
is the type of the value. For example, a dictionary with Int
keys and Bool
values has type {Int: Bool}
.
In a dictionary, all keys must have a type that is a subtype of the dictionary's key type (K
), and all values must have a type that is a subtype of the dictionary's value type (V
).
_24// Declare a constant that has type `{Int: Bool}`,_24// a dictionary mapping integers to booleans._24//_24let booleans = {_24 1: true,_24 0: false_24}_24_24// Declare a constant that has type `{Bool: Int}`,_24// a dictionary mapping booleans to integers._24//_24let integers = {_24 true: 1,_24 false: 0_24}_24_24// Mixing keys with different types, and mixing values with different types,_24// is possible by declaring the expected dictionary type with the common supertype_24// of all keys, and the common supertype of all values._24//_24let mixedValues: {String: AnyStruct} = {_24 "a": 1,_24 "b": true_24}
Dictionary types are covariant in their key and value types. For example, {Int: String}
is a subtype of {AnyStruct: String}
and also a subtype of {Int: AnyStruct}
. This is safe because dictionaries are value types and not reference types.
Dictionary Access
To get the value for a specific key from a dictionary, the following access syntax can be used: the dictionary is followed by an opening square bracket [
, the key, and ends with a closing square bracket ]
.
Accessing a key returns an optional: if the key is found in the dictionary, the value for the given key is returned; and if the key is not found, nil
is returned.
_17// Declare a constant that has type `{Int: Bool}`,_17// a dictionary mapping integers to booleans._17//_17let booleans = {_17 1: true,_17 0: false_17}_17_17// The result of accessing a key has type `Bool?`._17//_17booleans[1] // is `true`_17booleans[0] // is `false`_17booleans[2] // is `nil`_17_17// Invalid: Accessing a key which does not have type `Int`._17//_17booleans["1"]
_12// Declare a constant that has type `{Bool: Int}`,_12// a dictionary mapping booleans to integers._12//_12let integers = {_12 true: 1,_12 false: 0_12}_12_12// The result of accessing a key has type `Int?`_12//_12integers[true] // is `1`_12integers[false] // is `0`
To set the value for a key of a dictionary, the access syntax can be used as well.
_13// Declare a constant that has type `{Int: Bool}`,_13// a dictionary mapping booleans to integers._13//_13let booleans = {_13 1: true,_13 0: false_13}_13_13// Assign new values for the keys `1` and `0`._13//_13booleans[1] = false_13booleans[0] = true_13// `booleans` is `{1: false, 0: true}`
Dictionary fields and functions
-
_10let length: Int
The number of entries in the dictionary.
_10// Declare a dictionary mapping strings to integers._10let numbers = {"fortyTwo": 42, "twentyThree": 23}_10_10// Find the number of entries of the dictionary._10let length = numbers.length_10_10// `length` is `2` -
_10access(Mutate | Insert)_10fun insert(key: K, _ value: V): V?
Inserts the given value of type
V
into the dictionary under the givenkey
of typeK
.The inserted key must have the same type as the dictionary's key type, and the inserted value must have the same type as the dictionary's value type.
Returns the previous value as an optional if the dictionary contained the key; otherwise, returns
nil
.Updates the value if the dictionary already contained the key.
This function mutates the dictionary.
_11// Declare a dictionary mapping strings to integers._11let numbers = {"twentyThree": 23}_11_11// Insert the key `"fortyTwo"` with the value `42` into the dictionary._11// The key did not previously exist in the dictionary,_11// so the result is `nil`_11//_11let old = numbers.insert(key: "fortyTwo", 42)_11_11// `old` is `nil`_11// `numbers` is `{"twentyThree": 23, "fortyTwo": 42}` -
_10access(Mutate | Remove)_10fun remove(key: K): V?
Removes the value for the given
key
of typeK
from the dictionary.Returns the value of type
V
as an optional if the dictionary contained the key; otherwise, returnsnil
.This function mutates the dictionary.
_19// Declare a dictionary mapping strings to integers._19let numbers = {"fortyTwo": 42, "twentyThree": 23}_19_19// Remove the key `"fortyTwo"` from the dictionary._19// The key exists in the dictionary,_19// so the value associated with the key is returned._19//_19let fortyTwo = numbers.remove(key: "fortyTwo")_19_19// `fortyTwo` is `42`_19// `numbers` is `{"twentyThree": 23}`_19_19// Remove the key `"oneHundred"` from the dictionary._19// The key does not exist in the dictionary, so `nil` is returned._19//_19let oneHundred = numbers.remove(key: "oneHundred")_19_19// `oneHundred` is `nil`_19// `numbers` is `{"twentyThree": 23}` -
_10let keys: [K]
Returns an array of the keys of type
K
in the dictionary. This does not modify the dictionary — it just returns a copy of the keys as an array. If the dictionary is empty, this returns an empty array. The ordering of the keys is undefined._10// Declare a dictionary mapping strings to integers._10let numbers = {"fortyTwo": 42, "twentyThree": 23}_10_10// Find the keys of the dictionary._10let keys = numbers.keys_10_10// `keys` has type `[String]` and is `["fortyTwo","twentyThree"]` -
_10let values: [V]
Returns an array of the values of type
V
in the dictionary. This does not modify the dictionary — it just returns a copy of the values as an array. If the dictionary is empty, this returns an empty array.This field is not available if
V
is a resource type._10// Declare a dictionary mapping strings to integers._10let numbers = {"fortyTwo": 42, "twentyThree": 23}_10_10// Find the values of the dictionary._10let values = numbers.values_10_10// `values` has type [Int] and is `[42, 23]` -
_10access(all)_10view fun containsKey(key: K): Bool
Returns true if the given key of type
K
is in the dictionary._15// Declare a dictionary mapping strings to integers._15let numbers = {"fortyTwo": 42, "twentyThree": 23}_15_15// Check if the dictionary contains the key "twentyFive"._15let containsKeyTwentyFive = numbers.containsKey("twentyFive")_15// `containsKeyTwentyFive` is `false`_15_15// Check if the dictionary contains the key "fortyTwo"._15let containsKeyFortyTwo = numbers.containsKey("fortyTwo")_15// `containsKeyFortyTwo` is `true`_15_15// Invalid: Check if the dictionary contains the key 42._15// This results in a type error, as the key type of the dictionary is `String`._15//_15let containsKey42 = numbers.containsKey(42) -
_10access(all)_10fun forEachKey(_ function: fun(K): Bool): Void
Iterates through all the keys in the dictionary, exiting early if the passed function returns false. This is more efficient than calling
.keys
and iterating over the resulting array, since an intermediate allocation is avoided. The order of key iteration is undefined, similar to.keys
._18// Take in a targetKey to look for, and a dictionary to iterate through._18fun myContainsKey(targetKey: String, dictionary: {String: Int}) {_18// Declare an accumulator that we'll capture inside a closure._18var found = false_18_18// At each step, `key` will be bound to another key from `dictionary`._18dictionary.forEachKey(fun (key: String): Bool {_18found = key == targetKey_18_18// The returned boolean value, signals whether to continue iterating._18// This allows for control flow during the iteration process:_18// true = `continue`_18// false = `break`_18return !found_18})_18_18return found_18}
Dictionary keys
Dictionary keys must be hashable and equatable.
Most of the built-in types, like booleans and integers, are hashable and equatable, so can be used as keys in dictionaries.