交换键和值的字典扩展 - Swift 4.1

Dictionary Extension that Swaps Keys & Values - Swift 4.1

词典扩展 - 交换词典键和值

Swift4.1,Xcode9.3

我想创建一个函数,它将接受 Dictionary 和 return 所述字典,但将其值作为键,将其键作为其各自的值。到目前为止,我已经创建了一个函数来执行此操作,但我无法终生将它变成 Dictionaryextension


我的函数

func swapKeyValues<T, U>(of dict: [T : U]) -> [U  : T] {
    let arrKeys = Array(dict.keys)
    let arrValues = Array(dict.values)
    var newDict = [U : T]()
    for (i,n) in arrValues.enumerated() {
        newDict[n] = arrKeys[i]
    }
    return newDict
}

使用示例:

 let dict = [1 : "a", 2 : "b", 3 : "c", 4 : "d", 5 : "e"]
 let newDict = swapKeyValues(of: dict)
 print(newDict) //["b": 2, "e": 5, "a": 1, "d": 4, "c": 3]

理想:

 let dict = [1 : "a", 2 : "b", 3 : "c", 4 : "d", 5 : "e"]

 //I would like the function in the extension to be called swapped()
 print(dict.swapped()) //["b": 2, "e": 5, "a": 1, "d": 4, "c": 3]

如何实现这个理想?


明确地说,除非值符合 Hashable 协议,否则您所要求的是不可能的。所以,有条件的延期就是您要找的。

extension Dictionary where Value: Hashable {
    func keyValueSwapped() -> [Value:Key] {
        var newDict = [Value:Key]()
        keys.forEach { (key) in
            let value = self[key]!
            newDict[value] = key
        }
        return newDict
    }
}

Dictionary 的扩展可能是这样的,成为键的 value 必须限制为 Hashable

extension Dictionary where Value : Hashable {

    func swapKeyValues() -> [Value : Key] {
        assert(Set(self.values).count == self.keys.count, "Values must be unique")
        var newDict = [Value : Key]()
        for (key, value) in self {
            newDict[value] = key
        }
        return newDict
    }
}

let dict = [1 : "a", 2 : "b", 3 : "c", 4 : "d", 5 : "e"]
let newDict = dict.swapKeyValues()
print(newDict)

正如其他答案中已经解释的那样,Value 类型必须是 限制为Hashable,否则不能为Key 新词典。

还必须决定如何在源字典中重复值 应该处理。

为了实现,可以将源字典映射到 交换键和值的序列,并将其传递给 初始值设定项之一

它们在处理重复键的方式上有所不同:第一个中止 有一个运行时异常,第二个调用闭包 解决冲突。

所以一个简单的实现是

extension Dictionary where Value: Hashable {

    func swapKeyValues() -> [Value : Key] {
        return Dictionary<Value, Key>(uniqueKeysWithValues: lazy.map { ([=10=].value, [=10=].key) })
    }
}

(映射源字典 lazily 避免创建一个 所有交换 key/value 元组的中间数组。)

示例:

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]

如果源字典有重复值,这将崩溃。 这是一个接受源中重复值的变体 字典(“后来”的值会覆盖“较早”的值):

extension Dictionary where Value: Hashable {

    func swapKeyValues() -> [Value : Key] {
        return Dictionary<Value, Key>(lazy.map { ([=12=].value, [=12=].key) }, uniquingKeysWith: {  })
    }
}

示例:

let dict = [1 : "a", 2 : "b", 3 : "b"]
print(dict.swapKeyValues()) // ["b": 3, "a": 1]

另一种选择是将其实现为字典 初始值设定项。例如:

extension Dictionary where Value: Hashable {

    init?(swappingKeysAndValues dict: [Value:  Key]) {
        self.init(uniqueKeysWithValues: dict.lazy.map( { ([=14=].value, [=14=].key) }))
    }
}

在源字典中出现重复值的情况下崩溃, 或者作为抛出初始化器

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 { ([=15=].value, [=15=].key) },
                          uniquingKeysWith: { _,_ in throw DuplicateValuesError() })
    }
}

或作为可失败的初始化器:

extension Dictionary where Value: Hashable {

    struct DuplicateValuesError: Error { }

    init?(swappingKeysAndValues dict: [Value:  Key]) {
        do {
            try self.init(dict.lazy.map { ([=16=].value, [=16=].key) },
                          uniquingKeysWith: { _,_ in throw DuplicateValuesError() })
        } catch {
            return nil
        }
    }
}

示例(对于可失败初始化器):

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]
}

或者,如果您确定不会出现重复值:

let dict = [1 : "a", 2 : "b", 3 : "c", 4 : "d", 5 : "e"]
let newDict = Dictionary(swappingKeysAndValues: dict)!

您可以映射字典以交换键和值,并使用 Dictionary(uniqueKeysWithValues:) 初始值设定项创建一个新字典:

let dict = ["Key1": "Value1", "Key2": "Value2", ...]
let swappedDict = Dictionary(uniqueKeysWithValues: dict.map {(key, value) in return (value, key)})

请注意 Value 类型应符合 Hashable 协议,并且初始化程序会在重复键的情况下抛出运行时异常。如果原始字典可能有重复值,请改用 Dictionary(_:uniquingKeysWith:) 初始值设定项。