An ordered collection of key-value pairs.
import OrderedCollections
@frozen struct OrderedDictionary<Key: Hashable, Value>Like the standard Dictionary, ordered dictionaries use a hash table to
ensure that no two entries have the same keys, and to efficiently look up
values corresponding to specific keys. However, like an Array (and
unlike Dictionary), ordered dictionaries maintain their elements in a
particular user-specified order, and they support efficient random-access
traversal of their entries.
OrderedDictionary is a useful alternative to Dictionary when the order
of elements is important, or when you need to be able to efficiently access
elements at various positions within the collection.
You can create an ordered dictionary with any key type that conforms to the
Hashable protocol.
let responses: OrderedDictionary = [
200: "OK",
403: "Access forbidden",
404: "File not found",
500: "Internal server error",
]Two ordered dictionaries are considered equal if they contain the same
elements, and in the same order. This matches the concept of equality of
an Array, and it is different from the unordered Dictionary.
let a: OrderedDictionary = [1: "one", 2: "two"]
let b: OrderedDictionary = [2: "two", 1: "one"]
a == b // false
b.swapAt(0, 1) // `b` now has value [1: "one", 2: "two"]
a == b // true(OrderedDictionary only conforms to Equatable when its Value is
equatable.)
OrderedDictionary provides many of the same operations as Dictionary.
For example, you can look up and add/remove values using the familiar key-based subscript, returning an optional value:
var dictionary: OrderedDictionary<String, Int> = [:]
dictionary["one"] = 1
dictionary["two"] = 2
dictionary["three"] // nil
// dictionary is now ["one": 1, "two": 2]If a new entry is added using the subscript setter, it gets appended to the end of the dictionary. (So that by default, the dictionary contains its elements in the order they were originally inserted.)
OrderedDictionary also implements the variant of this subscript that takes
a default value. Like with Dictionary, this is useful when you want to
perform in-place mutations on values:
let text = "short string"
var counts: OrderedDictionary<Character, Int> = [:]
for character in text {
counts[character, default: 0] += 1
}
// counts is ["s": 2, "h": 1, "o": 1,
// "r": 2, "t": 2, " ": 1,
// "i": 1, "n": 1, "g": 1]If the Value type implements reference semantics, or when you need to
perform a series of individual mutations on the values, the closure-based
updateValue(forKey:default:with:) method provides an easier-to-use
alternative to the defaulted key-based subscript.
let text = "short string"
var counts: OrderedDictionary<Character, Int> = [:]
for character in text {
counts.updateValue(forKey: character, default: 0) { value in
value += 1
}
}
// Same result as before(This isn't currently available on the regular Dictionary.)
The Dictionary type's original updateValue(_:forKey:) method is also
available, and so is index(forKey:), grouping/uniquing initializers
(init(uniqueKeysWithValues:), init(_:uniquingKeysWith:),
init(grouping:by:)), methods for merging one dictionary with another
(merge, merging), filtering dictionary entries (filter(_:)),
transforming values (mapValues(_:)), and a combination of these two
(compactMapValues(_:)).
Ordered dictionaries use integer indices representing offsets from the
beginning of the collection. However, to avoid ambiguity between key-based
and indexing subscripts, OrderedDictionary doesn't directly conform to
Collection. Instead, it only conforms to Sequence, and provides a
random-access collection view over its key-value pairs:
responses[0] // `nil` (key-based subscript)
responses.elements[0] // `(200, "OK")` (index-based subscript)Because ordered dictionaries need to maintain unique keys, neither
OrderedDictionary nor its elements view can conform to the full
MutableCollection or RangeReplaceableCollection protocols. However, they
are able to partially implement requirements: they support mutations
that merely change the order of elements, or just remove a subset of
existing members:
// Permutation operations from MutableCollection:
func swapAt(_ i: Index, _ j: Index)
func partition(by predicate: (Element) throws -> Bool) rethrows -> Index
func sort() where Element: Comparable
func sort(by predicate: (Element, Element) throws -> Bool) rethrows
func shuffle()
func shuffle<T: RandomNumberGenerator>(using generator: inout T)
// Removal operations from RangeReplaceableCollection:
func removeAll(keepingCapacity: Bool = false)
func remove(at index: Index) -> Element
func removeSubrange(_ bounds: Range<Int>)
func removeLast() -> Element
func removeLast(_ n: Int)
func removeFirst() -> Element
func removeFirst(_ n: Int)
func removeAll(where shouldBeRemoved: (Element) throws -> Bool) rethrowsOrderedDictionary also implements reserveCapacity(_) from
RangeReplaceableCollection, to allow for efficient insertion of a known
number of elements. (However, unlike Array and Dictionary,
OrderedDictionary does not provide a capacity property.)
Like the standard Dictionary, OrderedDictionary provides keys and
values properties that provide lightweight views into the corresponding
parts of the dictionary.
The keys collection is of type OrderedSet<Key>, containing all the keys
in the original dictionary.
let d: OrderedDictionary = [2: "two", 1: "one", 0: "zero"]
d.keys // [2, 1, 0] as OrderedSet<Int>The keys property is read-only, so you cannot mutate the dictionary
through it. However, it returns an ordinary ordered set value, which can be
copied out and then mutated if desired. (Such mutations won't affect the
original dictionary value.)
The values collection is a mutable random-access collection of the values
in the dictionary:
d.values // "two", "one", "zero"
d.values[2] = "nada"
// `d` is now [2: "two", 1: "one", 0: "nada"]
d.values.sort()
// `d` is now [2: "nada", 1: "one", 0: "two"]Both views store their contents in regular Array values, accessible
through their elements property.
Like the standard Dictionary type, the performance of hashing operations
in OrderedDictionary is highly sensitive to the quality of hashing
implemented by the Key type. Failing to correctly implement hashing can
easily lead to unacceptable performance, with the severity of the effect
increasing with the size of the hash table.
In particular, if a certain set of keys all produce the same hash value, then hash table lookups regress to searching an element in an unsorted array, i.e., a linear operation. To ensure hashed collection types exhibit their target performance, it is important to ensure that such collisions cannot be induced merely by adding a particular list of keys to the dictionary.
The easiest way to achieve this is to make sure Key implements hashing
following Hashable's documented best practices. The conformance must
implement the hash(into:) requirement, and every bit of information that
is compared in == needs to be combined into the supplied Hasher value.
When used correctly, Hasher produces high-quality, randomly seeded hash
values that prevent repeatable hash collisions.
When Key correctly conforms to Hashable, key-based lookups in an ordered
dictionary is expected to take O(1) equality checks on average. Hash
collisions can still occur organically, so the worst-case lookup performance
is technically still O(n) (where n is the size of the dictionary);
however, long lookup chains are unlikely to occur in practice.
An ordered dictionary consists of an ordered set of keys, alongside a
regular Array value that contains their associated values.