如何在Swift中合并两个字典实例?

141

如何使用Swift将一个字典(Dictionary)追加到另一个字典(Dictionary)中?

我正在使用AlamoFire库向REST服务器发送JSON内容。

字典1(Dictionary 1)

var dict1: [String: AnyObject] = [
    kFacebook: [
        kToken: token
    ]
]

词典2

var dict2: [String: AnyObject] = [
    kRequest: [
        kTargetUserId: userId
    ]
]

我怎样将这两个字典合并成一个新的字典,如下所示?

let parameters: [String: AnyObject] = [
    kFacebook: [
        kToken: token
    ],
    kRequest: [
        kTargetUserId: userId
    ]
]

我尝试使用dict1 += dict2,但是我得到了一个编译错误:

二元运算符'+='不能应用于两个 '[String : AnyObject]' 的操作数


2
可能是以下问题的重复:如何将一个字典中的项添加到另一个字典中 - Cœur
1
https://developer.apple.com/documentation/swift/dictionary/3127171-merge - Raunak
嘿@the-nomad,你能把你接受的答案移到赞数超过两倍的那个答案上吗?谢谢:-)。 - blackjacx
11个回答

272

我喜欢这种方法:

dicFrom.forEach { (key, value) in dicTo[key] = value }

Swift 4和5

在Swift 4中,苹果公司引入了一种更好的方法来合并两个字典:

let dictionary = ["a": 1, "b": 2]
let newKeyValues = ["a": 3, "b": 4]

let keepingCurrent = dictionary.merging(newKeyValues) { (current, _) in current }
// ["b": 2, "a": 1]

let replacingCurrent = dictionary.merging(newKeyValues) { (_, new) in new }
// ["b": 4, "a": 3]

在大多数操作容器的函数中,您有两个选项:

  • merge 改变现有字典
  • merging 返回新的字典

2
这很棒。它允许选择整个字典。是否有一种方法可以添加自定义的_per key_逻辑? - mfaani
你能否发布一个链接,例如 GitHub Gist,以说明你确切想要什么(或者解释一下)? - blackjacx
我同意mfaani的观点,我想要删除符合特定条件的新键值。我猜过滤器可以用于传入字典以进行一些修剪... - clearlight

90

我应该把这个“扩展(extension)”放在哪里,以便在我的代码中任何地方都可以使用这个“函数(function)”? 抱歉,我是个新手! - The Nomad
1
将其放入项目中的任何文件中。 - Shuo
如果值也是一个字典,如何进行深度合并? - Rodrigo Ruiz
3
自从Dictionary拥有了自己的合并函数后,这个扩展程序就不再需要了。请参考https://dev59.com/A18d5IYBdhLWcg3wiSjI#43615143。 - blackjacx
如果d1没有键/值,我认为这将无法使用。 - Adrian Bartholomew
显示剩余2条评论

19

对于Swift>= 2.2:
let parameters = dict1.reduce(dict2) { r, e in var r = r; r[e.0] = e.1; return r }

对于Swift < 2.2:
let parameters = dict1.reduce(dict2) { (var r, e) in r[e.0] = e.1; return r }

Swift 4有一个新函数:
let parameters = dict1.reduce(into: dict2) { (r, e) in r[e.0] = e.1 }

挖掘标准库非常重要:mapreducedropFirstforEach等是简洁代码的主要组成部分。函数式编程很有趣!


Swift 2.2编译器会发出警告:“'var'参数已被弃用,并将在Swift 3中删除”。 - Dheeraj Vepakomma

14
extension Dictionary {
    static func +=(lhs: inout Self, rhs: Self) {
        lhs.merge(rhs) { _ , new in new }
    }
    static func +=<S: Sequence>(lhs: inout Self, rhs: S) where S.Element == (Key, Value) {
        lhs.merge(rhs) { _ , new in new }
    }
}

var dic = ["test1": 1]
dic += ["test2": 2]
dic  // ["test1": 1, "test2": 2]
dic += [("test2", 3),("test3", 4)]
dic  // ["test3": 4, "test1": 1, "test2": 3]

9

SequenceType.forEach(由Dictionary实现)提供了一种优雅的解决方案,可以将一个字典的元素添加到另一个字典中。

dic1.forEach { dic2[$0] = $1 }

例如
func testMergeDictionaries() {
    let dic1 = [1:"foo"]
    var dic2 = [2:"bar"]

    dic1.forEach { dic2[$0] = $1 }

    XCTAssertEqual(dic2[1], "foo")
}

4

我的需求有所不同,我希望合并而不是覆盖。

merging:
    ["b": [1, 2], "s": Set([5, 6]), "a": 1, "d": ["x": 2]]
with
    ["b": [3, 4], "s": Set([6, 7]), "a": 2, "d": ["y": 4]]
yields:
    ["b": [1, 2, 3, 4], "s": Set([5, 6, 7]), "a": 2, "d": ["y": 4, "x": 2]]

我本希望能找到一种更简单的解决方案,但最终我采用了这个方法。挑战在于从动态类型转换到静态类型,我使用协议来解决这个问题。
值得注意的是,当您使用字典字面量语法时,实际上会获得基础类型,这些类型不会使用协议扩展。我放弃了支持它们的努力,因为我找不到一种容易验证集合元素统一性的方法。
import UIKit


private protocol Mergable {
    func mergeWithSame<T>(right: T) -> T?
}



public extension Dictionary {

    /**
    Merge Dictionaries

    - Parameter left: Dictionary to update
    - Parameter right:  Source dictionary with values to be merged

    - Returns: Merged dictionay
    */


    func merge(right:Dictionary) -> Dictionary {
        var merged = self
        for (k, rv) in right {

            // case of existing left value
            if let lv = self[k] {

                if let lv = lv as? Mergable where lv.dynamicType == rv.dynamicType {
                    let m = lv.mergeWithSame(rv)
                    merged[k] = m
                }

                else if lv is Mergable {
                    assert(false, "Expected common type for matching keys!")
                }

                else if !(lv is Mergable), let _ = lv as? NSArray {
                    assert(false, "Dictionary literals use incompatible Foundation Types")
                }

                else if !(lv is Mergable), let _ = lv as? NSDictionary {
                    assert(false, "Dictionary literals use incompatible Foundation Types")
                }

                else {
                    merged[k] = rv
                }
            }

                // case of no existing value
            else {
                merged[k] = rv
            }
        }

        return merged
    }
}




extension Array: Mergable {

    func mergeWithSame<T>(right: T) -> T? {

        if let right = right as? Array {
            return (self + right) as? T
        }

        assert(false)
        return nil
    }
}


extension Dictionary: Mergable {

    func mergeWithSame<T>(right: T) -> T? {

        if let right = right as? Dictionary {
            return self.merge(right) as? T
        }

        assert(false)
        return nil
    }
}


extension Set: Mergable {

    func mergeWithSame<T>(right: T) -> T? {

        if let right = right as? Set {
            return self.union(right) as? T
        }

        assert(false)
        return nil
    }
}



var dsa12 = Dictionary<String, Any>()
dsa12["a"] = 1
dsa12["b"] = [1, 2]
dsa12["s"] = Set([5, 6])
dsa12["d"] = ["c":5, "x": 2]


var dsa34 = Dictionary<String, Any>()
dsa34["a"] = 2
dsa34["b"] = [3, 4]
dsa34["s"] = Set([6, 7])
dsa34["d"] = ["c":-5, "y": 4]


//let dsa2 = ["a": 1, "b":a34]
let mdsa3 = dsa12.merge(dsa34)
print("merging:\n\t\(dsa12)\nwith\n\t\(dsa34) \nyields: \n\t\(mdsa3)")

2

合并两个字典的 Swift 4+ 解决方案:

let dict1 = ["a": 1, "b": 2]
let dict2 = ["a": 3, "c": 4]

//In case of duplicate keys, values of dict1 will replace dict2
 let result1 = dict1.merging(dict2) { (current, _) in current } //["a": 1, "b": 2, "c": 4]

//In case of duplicate keys, values  of dict2 will replace dict1
 let result2 = dict1.merging(dict2) { (_, new) in new } //["a": 3, "b": 2, "c": 4]

2
您可以使用以下代码在Swift中合并两个字典实例:

您可以使用以下代码在Swift中合并两个字典实例:

extension Dictionary {
    func merge(dict: Dictionary<Key,Value>) -> Dictionary<Key,Value> {
        var mutableCopy = self
        for (key, value) in dict {
            // If both dictionaries have a value for same key, the value of the other dictionary is used.
            mutableCopy[key] = value
        }
        return mutableCopy
    }
}

2
你应该将其重命名为 merging。根据 Swift 命名规范,merge 将是可变版本。请注意,这两种方法已经有本地实现。 - Leo Dabus

2

尝试这种方法

    let dict1: [String: AnyObject] = ["kFacebook": ["kToken": "token"]]
    let dict2: [String: AnyObject] = ["kRequest": ["kTargetUserId": "userId"]]

    var combinedAttributes : NSMutableDictionary!

    combinedAttributes = NSMutableDictionary(dictionary: dict1)

    combinedAttributes.addEntriesFromDictionary(dict2)

    println(combinedAttributes)

它将会打印以下内容:
{
kFacebook =     {
    kToken = token;
};
kRequest =     {
    kTargetUserId = userId;
};

希望这能帮到你!


0

为什么不使用 reduce:

let combined = dict1.reduce(into: dict2, { partialResult, dict1 in
    partialResult.updateValue(dict1.value, forKey: dict1.key)
})

复杂度
O(n),其中n是序列的长度。

“merging” 的工作方式相同,但语法要简单得多。 - Gargo
我喜欢这种方法。使用$0和$1使它简洁明了。 - Sentry.co
我们还可以将这段代码添加到一个加法运算符中,就像func + <Self>(a: Self, b: Self) -> Self {...}这样。 - Sentry.co

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接