As already explained in the other answers, the Value
type must be
restricted to be Hashable
, otherwise it can not be the Key
for the
new dictionary.
Also one has to decide how duplicate values in the source dictionary
should be handled.
For the implementation one can map the source dictionary to a
sequence with keys and values exchanged, and passes that to
one of the initializers
These differ in how duplicate keys are treated: The first one aborts
with a runtime exception, the second one calls the closure for
conflict resolution.
So a simple implementation is
extension Dictionary where Value: Hashable {
func swapKeyValues() -> [Value : Key] {
return Dictionary<Value, Key>(uniqueKeysWithValues: lazy.map { ($0.value, $0.key) })
}
}
(Mapping the source dictionary lazily avoids the creation of an
intermediate array with all swapped key/value tuples.)
Example:
let dict = [1 : "a", 2 : "b", 3 : "c", 4 : "d", 5 : "e"]
print(dict.swapKeyValues()) //["b": 2, "e": 5, "a": 1, "d": 4, "c": 3]
This will crash if the source dictionary has duplicate values.
Here is a variant which accepts duplicate values in the source
dictionary (and “later” values overwrite “earlier” ones):
extension Dictionary where Value: Hashable {
func swapKeyValues() -> [Value : Key] {
return Dictionary<Value, Key>(lazy.map { ($0.value, $0.key) }, uniquingKeysWith: { $1 })
}
}
Example:
let dict = [1 : "a", 2 : "b", 3 : "b"]
print(dict.swapKeyValues()) // ["b": 3, "a": 1]
Another option would be to implement this as a dictionary
initializer. For example:
extension Dictionary where Value: Hashable {
init?(swappingKeysAndValues dict: [Value: Key]) {
self.init(uniqueKeysWithValues: dict.lazy.map( { ($0.value, $0.key) }))
}
}
which crashes in the case of duplicate values in the source dictionary,
or as a throwing initializer
extension Dictionary where Value: Hashable {
struct DuplicateValuesError: Error, LocalizedError {
var errorDescription: String? {
return "duplicate value"
}
}
init(swappingKeysAndValues dict: [Value: Key]) throws {
try self.init(dict.lazy.map { ($0.value, $0.key) },
uniquingKeysWith: { _,_ in throw DuplicateValuesError() })
}
}
or as a failable initializer:
extension Dictionary where Value: Hashable {
struct DuplicateValuesError: Error { }
init?(swappingKeysAndValues dict: [Value: Key]) {
do {
try self.init(dict.lazy.map { ($0.value, $0.key) },
uniquingKeysWith: { _,_ in throw DuplicateValuesError() })
} catch {
return nil
}
}
}
Example (for the failable initializer):
let dict = [1 : "a", 2 : "b", 3 : "c", 4 : "d", 5 : "e"]
if let newDict = Dictionary(swappingKeysAndValues: dict) {
print(newDict) //["b": 2, "e": 5, "a": 1, "d": 4, "c": 3]
}
Or, if you are sure that no duplicate values occur:
let dict = [1 : "a", 2 : "b", 3 : "c", 4 : "d", 5 : "e"]
let newDict = Dictionary(swappingKeysAndValues: dict)!