Dictionary
's sequence initialiser
In Swift 4, assuming that the keys are guaranteed to be unique, you can simply say:
let array = [MyStruct(key: 0, value: "a"), MyStruct(key: 1, value: "b")]
let dict = Dictionary(uniqueKeysWithValues: array.lazy.map { ($0.key, $0.value) })
print(dict) // [0: "a", 1: "c"]
This is using the init(uniqueKeysWithValues:)
initialiser from SE-0165. It expects a sequence of key-value tuples, where the keys are guaranteed to be unique (you'll get a fatal error if they aren't). So in this case, we're applying a lazy transform to the elements in your array in order to get a lazy collection of key-value pairs.
If the keys aren't guaranteed to be unique, you'll need some way of deciding which of the possible values to use for the given key. To do this, you can use the init(_:uniquingKeysWith:)
initialiser from the same proposal, and pass a given function to determine which value to use for a given key upon a duplicate key arising.
The first argument to the uniquingKeysWith:
function is the value that's already in the dictionary, the second is the value attempting to be inserted.
For example, here we're overwriting the value each time a duplicate key occurs in the sequence:
let array = [MyStruct(key: 0, value: "a"), MyStruct(key: 0, value: "b"),
MyStruct(key: 1, value: "c")]
let keyValues = array.lazy.map { ($0.key, $0.value) }
let dict = Dictionary(keyValues, uniquingKeysWith: { _, latest in latest })
print(dict) // [0: "b", 1: "c"]
To keep the first value for a given key, and ignore any subsequent values for the same key, you'd want a uniquingKeysWith:
closure of { first, _ in first }
, giving a result of [0: "a", 1: "c"]
in this case.
Reduce with an inout
accumulator
Another possible option in Swift 4, assuming you wish to merge any duplicate keys by overwriting the value at each occurrence of the given key is to use reduce(into:_:)
, introduced in SE-0171.
Unlike reduce(_:_:)
, this method uses an inout
parameter for the accumulator in the combination function. This allows it to avoid the unnecessary copying of the accumulator that would otherwise occur at each iteration of reduce(_:_:)
when populating a dictionary accumulator. This therefore allows us to populate it in linear, rather than quadratic time.
You can use it like so:
let array = [MyStruct(key: 0, value: "a"), MyStruct(key: 0, value: "b"),
MyStruct(key: 1, value: "c")]
let dict = array.reduce(into: [:]) { $0[$1.key] = $1.value }
print(dict) // [0: "b", 1: "c"]
// with initial capacity to avoid resizing upon populating.
let dict2 = array.reduce(into: Dictionary(minimumCapacity: array.count)) { dict, element in
dict[element.key] = element.value
}
print(dict2) // [0: "b", 1: "c"]