Слияние словарей со значениями в виде массивов в Swift4 - PullRequest
1 голос
/ 28 февраля 2020

У меня есть три словаря, и я хочу объединить их. Пожалуйста, помогите!

Словарь 1:

"Abc": {
    "Def": {
        "Jkl": "xxx",
    }
}

Словарь 2:

"Abc": {
    "Ghi": {
        "Mno": "yyy",
    }
}

Словарь 3:

"Abc": {
    "Ghi": {
        "Pqr": "zzz"
    }
}

Я хочу получить словарь сгруппировать похожие значения вместе, и это должно выглядеть так:

"Abc": {
    "Def: {
        "Jkl": "xxx"
    },
    "Ghi": {
        "Mno": "yyy",
        "Pqr": "zzz"
    }
}

Ответы [ 3 ]

2 голосов
/ 28 февраля 2020

Используйте этот рекурсивный метод для объединения двух словарей

func deepMerge(a: [String: Any], b: [String: Any]) -> [String: Any] {
    var result = a
    for (key, value) in b {
        //Key matches
        if let aValue = result[key] {
            //Both values are dictionaries
            if let aValDict = aValue as? [String: Any], let bValDict = value as? [String: Any] {
                result[key] = deepMerge(a: aValDict, b: bValDict)
            } else {
                //One/both values aren't dictionaries
                print("Expected two dictionaries to merge")
            }
        } else {
            //Different keys
            return a.merging(b) { (_, new) in new }
        }
    }
    return result
}

Использование

let dict1 = ["Abc": ["Def": ["Jkl": "xxx"]]]
let dict2 = ["Abc": ["Ghi": ["Mno": "yyy"]]]
let dict3 = ["Abc": ["Ghi": ["Pqr": "zzz"]]]

var result = [String: Any]()
for dict in [dict1, dict2, dict3] {
    result = deepMerge(a: result, b: dict)
}
print(result)
//["Abc": ["Ghi": ["Pqr": "zzz", "Mno": "yyy"], "Def": ["Jkl": "xxx"]]]
1 голос
/ 28 февраля 2020

iOSDev решение хорошая и самоочевидная рекурсивная функция. Я бы добавил сюда еще один способ обработки словарей при использовании метода Swift merging (_: uniquingKeysWith:)

Создает словарь путем объединения пар ключ-значение в последовательности в словарь , используя комбинированное замыкание для определения значения для дубликатов ключей.

Доступно в Swift начиная с версии 4.2

Ваше решение может выглядеть изначально

func mergeStandard(_ one: [String: [String: [String: String]]],
                   _ two: [String: [String: [String: String]]]) -> [String: [String: [String: String]]] {
    return one.merging(two) {
        $0.merging($1) {
            $0.merging($1) { value1, value2 -> String in
                return value1 // which is logically wrong, as we should have both values, see the Further Steps section
            }
        }
    }
}

let a = ["Abc": ["Def": ["Jkl": "xxx"]]]
let b = ["Abc": ["Ghi": ["Mno": "yyy"]]]
let c = ["Abc": ["Ghi": ["Pqr": "zzz"]]]
let d = mergeStandard(a, b)
let e = mergeStandard(d, c)

print(e)
//["Abc": ["Def": ["Jkl": "xxx"], "Ghi": ["Mno": "yyy", "Pqr": "zzz"]]]

NB! при попытке ввода другого типа, например

let a = ["Abc": ["Def": ["Jkl": "xxx"]]]
let b = ["Abc": ["Ghi": ["Mno": "yyy", "Pqr": "qqq"]]]
let c = ["Abc": ["Ghi": ["Pqr": "zzz"]]]

Поскольку у вас есть две клавиши "Pqr". Приведенный выше код выберет значение "qqq" для клавиши "Pqr". Другой ответ - от iOSDev есть такая же проблема. Пожалуйста, посмотрите решение для этого случая ниже.


Дальнейшие действия

Я бы порекомендовал вам изменить структуру данных на [String: [String: [String: [String]]]], чтобы вы имели последнее значение как Sequence из String. И тогда вы можете использовать следующее:

func mergeUpgraded(_ one: [String: [String: [String: [String]]]],
                   _ two: [String: [String: [String: [String]]]]) -> [String: [String: [String: [String]]]] {
    return one.merging(two) {
        $0.merging($1) {
            $0.merging($1) { (arr1, arr2) -> [String] in
                var a = arr1
                var b = arr2
                a.append(contentsOf: b)
                return a
            }
        }
    }
}

let a = ["Abc": ["Def": ["Jkl": ["xxx"]]]]
let b = ["Abc": ["Ghi": ["Mno": "yyy", "Pqr": "qqq"]]]
let c = ["Abc": ["Ghi": ["Pqr": "zzz"]]]
let d = mergeUpgraded(a, b)
let e = mergeUpgraded(d, c)

print(e)
// ["Abc": ["Ghi": ["Mno": ["yyy"], "Pqr": ["qqq", "zzz"]], "Def": ["Jkl": ["xxx"]]]]
0 голосов
/ 03 марта 2020

Спасибо всем за помощь! Этот кусок кода работал для меня:

    private func deepMerge(_ d1: [String: Any], _ d2: [String: Any]) -> [String: Any] {
    var result = d1
    for (k2, v2) in d2 {
        if let v1 = result[k2] as? [String: Any], let v2 = v2 as? [String: Any] {
            result[k2] = deepMerge(v1, v2)
        } else {
            result[k2] = v2
        }
    }
    return result
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...