Расширение словаря, которое меняет ключи и значения - Swift 4.1 - PullRequest
0 голосов
/ 24 апреля 2018

Расширение словаря - замена словарных ключей и значений

Swift 4.1, Xcode 9.3

Я хочу создать функцию, которая будет принимать Dictionary и возвращать указанный словарь, но с его значениями какключи и его ключи в качестве соответствующих значений.До сих пор я сделал функцию для этого, но я не могу за всю свою жизнь превратить ее в extension для Dictionary.


Моя функция

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]

Как мне достичь этого идеала?


Ответы [ 3 ]

0 голосов
/ 24 апреля 2018

Расширение словаря может выглядеть так: 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)
0 голосов
/ 24 апреля 2018

Как уже объяснялось в других ответах, тип Value должен быть ограничен до Hashable, в противном случае он не может быть Key для нового словаря.

Также необходимо решитькак должны обрабатываться повторяющиеся значения в исходном словаре.

Для реализации можно сопоставить исходный словарь с последовательностью с обмененными ключами и значениями и передать ее одному из инициализаторов

Они отличаются в том, как обрабатываются дубликаты ключей: первый прерываетсяза исключением времени выполнения, второе вызывает закрытие для разрешения конфликта.

Таким образом, простая реализация -

extension Dictionary where Value: Hashable {

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

(отображение исходного словаря lazily позволяет избежать созданияпромежуточного массива со всеми заменяемыми наборами ключ / значение.)

Пример:

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 { ($0.value, $0.key) }, uniquingKeysWith: { $1 })
    }
}

Пример:

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( { ($0.value, $0.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 { ($0.value, $0.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 { ($0.value, $0.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)!
0 голосов
/ 24 апреля 2018

Чтобы было ясно, то, что вы просите, невозможно, если значения не соответствуют протоколу 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
    }
}
...