如何将项的字典添加到另一个字典中


172

Swift中的数组支持+ =运算符,可将一个数组的内容添加到另一个数组。有没有简单的方法可以做到这一点的字典?

例如:

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var combinedDict = ... (some way of combining dict1 & dict2 without looping)


fromDict.forEach {intoDict[$0] = $1}
Sazzad Hissain Khan

Answers:


171

您可以为定义+=运算符Dictionary,例如

func += <K, V> (left: inout [K:V], right: [K:V]) { 
    for (k, v) in right { 
        left[k] = v
    } 
}

1
哦,老兄,我为此寻找合适的通用声明非常费力,我尝试了除此以外的所有方法。但是您可以删除@assignmentreturn,您已经向左突变。编辑:实际上,即使我没有错误,我认为也@assignment应该保留。
罗兰

14
更多语法糖: func +=<K, V> (inout left: [K : V], right: [K : V]) { for (k, v) in right { left[k] = v } }
伊万·瓦维洛夫

48
@animal_chin因为我们必须自己实现一半的语言?是。印象深刻。不要误会我的意思,我喜欢运算符重载。我只是不喜欢有使用它应建立在基本功能。
devios1

2
@devios Haha然后将其作为请求发送给Swift回购:D既然显然苹果无法被
抢购

6
直接从SwifterSwift库提取public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) { rhs.forEach({ lhs[$0] = $1}) }
贾斯汀·

99

在Swift 4中,应该使用merging(_:uniquingKeysWith:)

例:

let dictA = ["x" : 1, "y": 2, "z": 3]
let dictB = ["x" : 11, "y": 22, "w": 0]

let resultA = dictA.merging(dictB, uniquingKeysWith: { (first, _) in first })
let resultB = dictA.merging(dictB, uniquingKeysWith: { (_, last) in last })

print(resultA) // ["x": 1, "y": 2, "z": 3, "w": 0]
print(resultB) // ["x": 11, "y": 22, "z": 3, "w": 0]

1
//可变:var dictA = [“ x”:1,“ y”:2,“ z”:3] var dictB = [“ x”:11,“ y”:22,“ w”:0] dictA。 merge(dictB,uniquingKeysWith:{(第一个,第一个_)})print(dictA)// [“ x”:1,“ y”:2,“ z”:3,“ w”:0]
muthukumar

1
此答案中显示的第二个示例等效于[NSMutableDictionary addEntriesFromDictionary:]
orj

92

怎么样

dict2.forEach { (k,v) in dict1[k] = v }

这会将dict2的所有键和值添加到dict1中。


43
不错的解决方案。稍微短一点:dict2.forEach {dict1 [$ 0] = $ 1}
Brett

1
这是一个很好的解决方案,但是对于Swift 4,您很可能会得到一个错误说明Closure tuple parameter '(key: _, value: _)' does not support destructuring(至少在撰写本文时)。一个人需要根据[此stackoverflow答案](stackoverflow.com/questions/44945967/…
重新构造

78

当前,在查看Swift标准库字典参考时,无法轻松地用另一本字典更新字典。

你可以写一个扩展来做

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

extension Dictionary {
    mutating func update(other:Dictionary) {
        for (key,value) in other {
            self.updateValue(value, forKey:key)
        }
    }
}

dict1.update(dict2)
// dict1 is now ["a" : "foo", "b" : "bar]

3
这是字典扩展的很好用!
Marc Attinasi 2014年

74

Swift 4提供了merging(_:uniquingKeysWith:),因此适合您的情况:

let combinedDict = dict1.merging(dict2) { $1 }

速记闭包返回$1,因此当与键冲突时将使用dict2的值。


1
只是想指出一点,这是我发现的最简洁,最接近Apple文档说明的内容- (void)addEntriesFromDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary;。关于如何处理重复项,它指出:“如果两个字典都包含相同的键,则接收字典对该键的先前值对象将被发送一个释放消息,而新值对象将取代它。” Swift版本,或在merge(_:uniquingKeysWith :)中,返回第二个值,$1与操作相同addEntriesFromDictionary
蒂姆·富夸

31

它没有内置在Swift库中,但是您可以添加运算符重载,例如:

func + <K,V>(left: Dictionary<K,V>, right: Dictionary<K,V>) 
    -> Dictionary<K,V> 
{
    var map = Dictionary<K,V>()
    for (k, v) in left {
        map[k] = v
    }
    for (k, v) in right {
        map[k] = v
    }
    return map
}

这会使+字典的运算符超载,您现在可以使用它为+运算符添加字典,例如:

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var dict3 = dict1 + dict2 // ["a": "foo", "b": "bar"]

1
您也可以使用+ =来更新字典(根据op问题)。
2014年

3
如果将参数声明为as ,则可以map删除第一个for (k, v)...循环,然后将其复制到其中。leftvarright
Nate Cook 2014年

2
@NateCook将使Dictionary发生突变,这对于+infix运算符而言不是预期的行为。
mythz 2014年

感谢那。对于我发布的示例代码,您的答案可能更准确,而根据我的问题,另一个答案更符合我的期望。我的糟糕,无论如何都给了你们两个;)
rustyshelf 2014年

2
@mythz并不是真正的变异,因为+运算符重载也不是的方法Dictionary,它是一个简单的函数。您对变量left参数所做的更改在函数外部将不可见。
Nate Cook

28

斯威夫特3:

extension Dictionary {

    mutating func merge(with dictionary: Dictionary) {
        dictionary.forEach { updateValue($1, forKey: $0) }
    }

    func merged(with dictionary: Dictionary) -> Dictionary {
        var dict = self
        dict.merge(with: dictionary)
        return dict
    }
}

let a = ["a":"b"]
let b = ["1":"2"]
let c = a.merged(with: b)

print(c) //["a": "b", "1": "2"]

6
稍微好一点func merged(with dictionary: Dictionary<Key,Value>) -> Dictionary<Key,Value> { var copy = self dictionary.forEach { copy.updateValue($1, forKey: $0) } return copy }
Alexander Vasenin '16

16

雨燕2.0

extension Dictionary {

    mutating func unionInPlace(dictionary: Dictionary) {
        dictionary.forEach { self.updateValue($1, forKey: $0) }
    }

    func union(var dictionary: Dictionary) -> Dictionary {
        dictionary.unionInPlace(self)
        return dictionary
    }
}

不能从像这样的非变异函数中调用变异函数
njzk2

union函数将传递给它的值为a var,这意味着可以对复制的字典进行突变。func union(dictionary: Dictionary) -> Dictionary { var dict2 = dictionary; dict2.unionInPlace(self); return dict2 }如果仅一行,它比整洁。
MaddTheSane '16

2
不推荐使用var参数,并将在Swift 3中将其删除。现在,执行此操作的首选方法是在主体中声明var :var dictionary = dictionary。从这里:github.com/apple/swift-evolution/blob/master/proposals/...
丹尼尔·伍德

为了使类型更安全,请添加<Key, Value>Dictionary
拉斐尔

12

一成不变的

我更喜欢将不可变字典与+运算符结合/统一,所以我实现了像这样:

// Swift 2
func + <K,V> (left: Dictionary<K,V>, right: Dictionary<K,V>?) -> Dictionary<K,V> {
    guard let right = right else { return left }
    return left.reduce(right) {
        var new = $0 as [K:V]
        new.updateValue($1.1, forKey: $1.0)
        return new
    }
}

let moreAttributes: [String:AnyObject] = ["Function":"authenticate"]
let attributes: [String:AnyObject] = ["File":"Auth.swift"]

attributes + moreAttributes + nil //["Function": "authenticate", "File": "Auth.swift"]    
attributes + moreAttributes //["Function": "authenticate", "File": "Auth.swift"]
attributes + nil //["File": "Auth.swift"]

可变的

// Swift 2
func += <K,V> (inout left: Dictionary<K,V>, right: Dictionary<K,V>?) {
    guard let right = right else { return }
    right.forEach { key, value in
        left.updateValue(value, forKey: key)
    }
}

let moreAttributes: [String:AnyObject] = ["Function":"authenticate"]
var attributes: [String:AnyObject] = ["File":"Auth.swift"]

attributes += nil //["File": "Auth.swift"]
attributes += moreAttributes //["File": "Auth.swift", "Function": "authenticate"]

5
我不明白为什么默认情况下它没有内置于swift中?
ioquatix

1
您是否打算在“不可变”解决方案中从左至右覆盖值?我认为您的意思是right.reduce(left),至少是imo的预期行为(这是第二个示例的行为)-即。["A":1] + ["A":2]应该输出["A":2]
ccwasden

输出对应于代码。我希望初始值像现在一样在右侧。
ricardopereira

12

现在无需任何字典扩展。Swift(Xcode 9.0+)字典为此提供了功能。在这里看看。以下是有关如何使用它的示例

  var oldDictionary = ["a": 1, "b": 2]
  var newDictionary = ["a": 10000, "b": 10000, "c": 4]

  oldDictionary.merge(newDictionary) { (oldValue, newValue) -> Int in
        // This closure return what value to consider if repeated keys are found
        return newValue 
  }
  print(oldDictionary) // Prints ["b": 10000, "a": 10000, "c": 4]

2
我为上述示例添加了一种功能样式:oldDictionary.merge(newDictionary) { $1 }
Andrej

11

使用扩展名的更具可读性的变体。

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

3
非常干净的解决方案!
user3441734 '16

11

你可以试试这个

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var temp = NSMutableDictionary(dictionary: dict1);
temp.addEntriesFromDictionary(dict2)

10

您也可以使用reduce合并它们。在操场上尝试

let d1 = ["a":"foo","b":"bar"]
let d2 = ["c":"car","d":"door"]

let d3 = d1.reduce(d2) { (var d, p) in
   d[p.0] = p.1
   return d
}

这看起来很有趣,但是dand是p什么?

1
d是reduce块每次迭代的持久性结果,p是要减少的集合元素。
farhadf

1
这似乎在迅速的3.0 beta中崩溃了
possen 2016年

迅速停用var参数3
Dmitry Klochkov

在这里提到的解决方案中,这是我最喜欢的解决方案。筛选/映射/减少再次赢得了简洁的解决方案。
gokeji '16

7

我推荐SwifterSwift库。但是,如果您不想使用整个库及其所有出色的附加功能,则可以利用它们的Dictionary扩展:

迅捷3+

public extension Dictionary {
    public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) {
        rhs.forEach({ lhs[$0] = $1})
    }
}

实际上,SE-110已还原,因此Swift 4版本应与Swift 3版本相同。
BallpointBen

5

您可以遍历键值组合ob想要合并的值,并通过updateValue(forKey :)方法添加它们:

dictionaryTwo.forEach {
    dictionaryOne.updateValue($1, forKey: $0)
}

现在,dictionaryTwo的所有值都添加到dictionaryOne中。


4

与@farhadf的答案相同,但被Swift 3采纳:

let sourceDict1 = [1: "one", 2: "two"]
let sourceDict2 = [3: "three", 4: "four"]

let result = sourceDict1.reduce(sourceDict2) { (partialResult , pair) in
    var partialResult = partialResult //without this line we could not modify the dictionary
    partialResult[pair.0] = pair.1
    return partialResult
}

4

Swift 3,字典扩展:

public extension Dictionary {

    public static func +=(lhs: inout Dictionary, rhs: Dictionary) {
        for (k, v) in rhs {
            lhs[k] = v
        }
    }

}

4

Swift 4的一些更简化的重载:

extension Dictionary {
    static func += (lhs: inout [Key:Value], rhs: [Key:Value]) {
        lhs.merge(rhs){$1}
    }
    static func + (lhs: [Key:Value], rhs: [Key:Value]) -> [Key:Value] {
        return lhs.merging(rhs){$1}
    }
}

3

您可以添加如下Dictionary扩展名

extension Dictionary {
    func mergedWith(otherDictionary: [Key: Value]) -> [Key: Value] {
        var mergedDict: [Key: Value] = [:]
        [self, otherDictionary].forEach { dict in
            for (key, value) in dict {
                mergedDict[key] = value
            }
        }
        return mergedDict
    }
}

然后使用简单的,如下:

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var combinedDict = dict1.mergedWith(dict2)
// => ["a": "foo", "b": "bar"]

如果您希望框架还包含一些更方便的功能,请签出HandySwift。只需将其导入到您的项目中,就可以使用上面的代码,而无需自己添加任何扩展到项目中。


安装库以使用单个功能是不好的做法
HackaZach 2016年

@HackaZach:我刚刚更新了答案,将框架的适当部分包括在内,以防止只需要这部分就可以包含整个库。对于那些想使用它的多个功能的人,我向它保留了提示。我希望这有助于保持良好的习惯!
Jeehut

3

不再需要扩展或任何其他功能。您可以这样写:

firstDictionary.merge(secondDictionary) { (value1, value2) -> AnyObject in
        return object2 // what you want to return if keys same.
    }


1

您可以使用bridgeToObjectiveC()函数使字典成为NSDictionary。

将如下所示:

var dict1 = ["a":"Foo"]
var dict2 = ["b":"Boo"]

var combinedDict = dict1.bridgeToObjectiveC()
var mutiDict1 : NSMutableDictionary! = combinedDict.mutableCopy() as NSMutableDictionary

var combineDict2 = dict2.bridgeToObjectiveC()

var combine = mutiDict1.addEntriesFromDictionary(combineDict2)

然后,您可以将NSDictionary(combine)转换回去或执行任何操作。


对不起,你到底是什么意思?
安东

只是一个偏爱。似乎很难在语言之间架起桥梁。最好坚持使用单一语言的范围,同时让obj-c更快死掉。
TruMan1 2016年

2
是的,我在Swift宣布的那天就字面上贴了这个答案。……所以有一个原因
Anton

1
import Foundation

let x = ["a":1]
let y = ["b":2]

let out = NSMutableDictionary(dictionary: x)
out.addEntriesFromDictionary(y)

结果是NSMutableDictionary而不是Swift类型的字典,但是使用它的语法是相同的(out["a"] == 1在这种情况下),因此只有在使用期望使用Swift字典的第三方代码时,才有问题需要类型检查。

简短的答案是您实际上必须循环。即使您没有明确输入它,这也是您正在调用的方法(addEntriesFromDictionary:此处)的作用。我建议如果您不清楚为什么会出现这种情况,则应考虑如何合并两个B树的叶子节点。

如果您确实确实需要Swift本机字典类型作为回报,我建议:

let x = ["a":1]
let y = ["b":2]

var out = x
for (k, v) in y {
    out[k] = v
}

这种方法的缺点是字典索引(尽管已完成)可能会在循环中重建几次,因此在实践中,这比NSMutableDictionary方法慢大约10倍。


1

所有这些响应都很复杂。这是我的Swift 2.2解决方案:

    //get first dictionnary
    let finalDictionnary : NSMutableDictionary = self.getBasicDict()
    //cast second dictionnary as [NSObject : AnyObject]
    let secondDictionnary : [NSObject : AnyObject] = self.getOtherDict() as [NSObject : AnyObject]
    //merge dictionnary into the first one
    finalDictionnary.addEntriesFromDictionary(secondDictionnary) 

不幸的是,这仅适用于NSMutableDictionary而不适用于本机Swift字典。我希望将其本机添加到Swift中。
克里斯·帕维格里奥

0

我的需求有所不同,我需要合并不完整的嵌套数据集而不会造成麻烦。

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)")

0

斯威夫特2.2

func + <K,V>(left: [K : V], right: [K : V]) -> [K : V] {
    var result = [K:V]()

    for (key,value) in left {
        result[key] = value
    }

    for (key,value) in right {
        result[key] = value
    }
    return result
}

如果放这个,您可以删除第一个循环:`var result = left`
NikeAlive

0

我只会使用Dollar库。

https://github.com/ankurp/Dollar/#merge---merge-1

将所有字典合并在一起,后一个字典将覆盖给定键的值

let dict: Dictionary<String, Int> = ["Dog": 1, "Cat": 2]
let dict2: Dictionary<String, Int> = ["Cow": 3]
let dict3: Dictionary<String, Int> = ["Sheep": 4]
$.merge(dict, dict2, dict3)
=> ["Dog": 1, "Cat": 2, "Cow": 3, "Sheep": 4]

5
jQuery又回来了!
本·辛克莱

0

这是我写的一个不错的扩展...

extension Dictionary where Value: Any {
    public func mergeOnto(target: [Key: Value]?) -> [Key: Value] {
        guard let target = target else { return self }
        return self.merging(target) { current, _ in current }
    }
}

使用:

var dict1 = ["cat": 5, "dog": 6]
var dict2 = ["dog": 9, "rodent": 10]

dict1 = dict1.mergeOnto(target: dict2)

然后,将dict1修改为

["cat": 5, "dog": 6, "rodent": 10]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.