【问题标题】:How do you add a Dictionary of items into another Dictionary如何将项目词典添加到另一个词典中
【发布时间】:2014-07-25 22:58:38
【问题描述】:

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

例如:

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

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

【问题讨论】:

标签: dictionary swift


【解决方案1】:

你可以使用,

func addAll(from: [String: Any], into: [String: Any]){
    from.forEach {into[$0] = $1}
}

【讨论】:

    【解决方案2】:

    不再需要扩展或任何额外的功能。 你可以这样写:

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

    【讨论】:

      【解决方案3】:

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

      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]
      
      

      【讨论】:

        【解决方案4】:

        你可以试试这个

        var dict1 = ["a" : "foo"]
        var dict2 = ["b" : "bar"]
        
        var temp = NSMutableDictionary(dictionary: dict1);
        temp.addEntriesFromDictionary(dict2)
        

        【讨论】:

          【解决方案5】:

          一些更精简的 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}
              }
          }
          

          【讨论】:

            【解决方案6】:

            Swift 4 提供merging(_:uniquingKeysWith:),所以对于您的情况:

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

            简写闭包返回$1,因此当与键发生冲突时将使用dict2的值。

            【讨论】:

            • 只是想指出这是我发现的最简洁和最接近 Apple 文档中- (void)addEntriesFromDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary; 的内容。关于如何处理重复项,它指出:“如果两个字典都包含相同的键,则接收字典的该键的先前值对象将被发送释放消息,并且新的值对象将取代它。”,所以在Swift 版本,或者在 merge(_:uniquingKeysWith:) 中,返回第二个值 $1,与 addEntriesFromDictionary 所做的相同。
            【解决方案7】:

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

            【讨论】:

            • //可变: var dictA = ["x" : 1, "y": 2, "z": 3] var dictB = ["x" : 11, "y": 22, "w": 0] dictA.merge(dictB, uniquingKeysWith: { (first, _) in first }) print(dictA) // ["x": 1, "y": 2, "z": 3, " w": 0]
            • 此答案中显示的第二个示例相当于[NSMutableDictionary addEntriesFromDictionary:]
            【解决方案8】:

            目前,查看字典的Swift Standard Library Reference,没有办法轻松地用另一个字典更新字典。

            你可以写一个扩展来做到这一点

            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]
            

            【讨论】:

            • 这是词典扩展的绝佳用途!
            【解决方案9】:

            现在不需要任何字典扩展。 Swift(Xcode 9.0+) 字典为此提供了功能。看看here。下面是一个如何使用它的示例

              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]
            

            【讨论】:

            • 我正在为上面的例子添加一个函数样式:oldDictionary.merge(newDictionary) { $1 }
            【解决方案10】:

            Swift 3,字典扩展:

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

            【讨论】:

              【解决方案11】:

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

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

              【讨论】:

              • 哦,伙计,我为找到合适的通用声明而苦苦挣扎,除了这个,我尝试了所有方法。但是你可以放弃@assignmentreturn,你已经在左边变异了。编辑:实际上,即使我没有收到任何错误,我认为 @assignment 应该留下来。
              • 更多语法糖:func +=&lt;K, V&gt; (inout left: [K : V], right: [K : V]) { for (k, v) in right { left[k] = v } }
              • @animal_chin 因为我们必须自己实现一半的语言?是的。印象深刻。不要误会我的意思,我喜欢运算符重载。我只是不喜欢将它用于应该内置的基本功能。
              • @devios 哈哈,然后将其作为 Swift repo 的拉取请求:D 因为显然 Apple 不能被嘲笑
              • 直接从SwifterSwift Library拉取:public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) { rhs.forEach({ lhs[$0] = $1}) }
              【解决方案12】:

              我推荐SwifterSwift Library。但是,如果您不想使用整个库及其所有重要的附加功能,您可以使用它们的 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版本一样。
              【解决方案13】:

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

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

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

              【讨论】:

                【解决方案14】:

                我只会使用 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]
                

                【讨论】:

                • jQuery 回来啦!
                【解决方案15】:

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

                【讨论】:

                  【解决方案16】:

                  斯威夫特 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"]
                  

                  【讨论】:

                  • 稍微好一点func merged(with dictionary: Dictionary&lt;Key,Value&gt;) -&gt; Dictionary&lt;Key,Value&gt; {var copy = selfdictionary.forEach { copy.updateValue($1, forKey: $0) }return copy}
                  【解决方案17】:

                  怎么样

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

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

                  【讨论】:

                  • 不错的解决方案。稍微短一点:dict2.forEach { dict1[$0] = $1 }
                  • 这是一个很好的解决方案,但对于 Swift 4,您很可能会收到一条错误消息,指出 Closure tuple parameter '(key: _, value: _)' does not support destructuring(至少在撰写本文时)。需要根据 [this stackoverflow answer] (stackoverflow.com/questions/44945967/…) 重构闭包:
                  【解决方案18】:

                  斯威夫特 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`
                  【解决方案19】:

                  您可以像这样添加 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:我刚刚更新了我的答案以包含框架的适当部分,以防止包含整个库,如果只需要这个小部分。我为想要使用多个功能的人保留了对框架的提示。我希望这有助于保持良好的做法!
                  【解决方案20】:

                  所有这些反应都很复杂。这是我对 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 中。
                  【解决方案21】:
                  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: here)将执行的操作。我建议如果您不清楚为什么会出现这种情况,您应该考虑如何合并两个 B 树的叶节点。

                  如果你真的需要一个 Swift 原生字典类型作为回报,我建议:

                  let x = ["a":1]
                  let y = ["b":2]
                  
                  var out = x
                  for (k, v) in y {
                      out[k] = v
                  }
                  

                  这种方法的缺点是字典索引 - 无论它是如何完成的 - 可能会在循环中重建多次,因此实际上这比 NSMutableDictionary 方法慢了大约 10 倍。

                  【讨论】:

                    【解决方案22】:

                    我的需求不同,我需要合并不完整的嵌套数据集而不造成破坏。

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

                    【讨论】:

                      【解决方案23】:

                      使用扩展的更易读的变体。

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

                      【讨论】:

                      • 非常好的和干净的解决方案!
                      【解决方案24】:

                      不可变

                      我更喜欢将不可变字典与 + 运算符组合/统一,因此我将其实现为:

                      // 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"]
                      

                      【讨论】:

                      • 我不明白为什么这不是默认内置到 swift 中的?
                      • 您是否打算在“不可变”解决方案中从左侧覆盖右侧的值?我认为您的意思是拥有right.reduce(left),至少这是imo 的预期行为(这是您的第二个示例的行为)-即。 ["A":1] + ["A":2] 应该输出["A":2]
                      • 输出对应的代码。我希望初始值是右侧,就像现在一样。
                      【解决方案25】:

                      斯威夫特 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
                          }
                      }
                      

                      【讨论】:

                      • 不能像这样从非变异函数调用变异函数
                      • union 函数的传入值为var,这意味着复制的字典可以被变异。它比func union(dictionary: Dictionary) -&gt; Dictionary { var dict2 = dictionary; dict2.unionInPlace(self); return dict2 } 干净一点,如果只有一行的话。
                      • var 参数已被弃用,将在 Swift 3 中删除。现在最好的方法是在正文中声明一个 var:var dictionary = dictionary。从这里:github.com/apple/swift-evolution/blob/master/proposals/…
                      • 为了使事情更加类型安全,请将 &lt;Key, Value&gt; 添加到 Dictionarys 中。
                      【解决方案26】:

                      您也可以使用 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
                      }
                      

                      【讨论】:

                      • 这看起来很有趣,但是dp 是什么?
                      • d 是reduce 块每次迭代的持久化结果,p 是被reduced 的集合元素。
                      • 这似乎在 swift 3.0 beta 中崩溃了
                      • var 参数在 swift 3 中已弃用
                      • 这是这里提到的解决方案中我最喜欢的解决方案。 Filter/map/reduce 再次凭借出色的简洁解决方案获胜。
                      【解决方案27】:

                      它没有内置在 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"]
                      

                      【讨论】:

                      • 您也可以使用 += 来更新 dict(根据操作问题)。
                      • 如果将left 参数声明为var,则可以取消map 并删除第一个for (k, v)... 循环,然后将right 中的值复制到其中。跨度>
                      • @NateCook 会改变 Dictionary 这不是 + 中缀运算符的预期行为。
                      • 谢谢。您的答案可能对我发布的示例代码更准确,而另一个更符合我的问题。我的错,无论如何都给了你们两个赞成票;)
                      • @mythz 它并不是真正的变异,因为+ 运算符重载不是Dictionary 的方法,它是一个简单的函数。您对变量 left 参数所做的更改将不会在函数外部可见。
                      【解决方案28】:

                      您可以使用 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 死得更快。
                      • 是的,我在 Swift 宣布的那天发布了这个答案......所以这是有原因的
                      猜你喜欢
                      • 2014-12-19
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-06-07
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多