2016-06-10 87 views
2

我想要一个通用函数来合并两个字典。它需要更全面,然后在How do you add a Dictionary of items into another Dictionary中描述。原因:我在字典中有对象,我只想更新一个特定的属性,而不是整个对象本身。 (具体来说,它们是具有数组属性的对象,如果对象存在,我需要追加到这个数组,或者用新数组创建一个新对象,我不能简单地检查并相应地覆盖整个对象,很感兴趣,在它的属性)合并两个字典的通用函数

我试图做到这一点使用功能的编程:

extension Dictionary { 
    func merge(withDictionary: Dictionary) -> Dictionary { 
     var returnDictionary = withDictionary // make a copy of dictionary (a dictionary is a struct in Swift) 
     // Merge self dictionary into returnDictionary 
     for key in self.keys { 
      // If there is already a value associated for this key. (In my concrete case I will need to append to the value object's list property.) 
      if let withDictionaryValue = returnDictionary[key], selfValue = self[key] { 

       // I NEED TO DO THIS HERE. 
       // 
       // returnDictionary[key]!.list = withDictionaryValue.list + selfValue.list 
       // 
       // CAN'T FIGURE OUT HOW TO DO THIS IN A GENERIC WAY. THIS GENERIC MERGE SHOULDN'T NEED TO KNOW THAT THIS PARTICULAR DICTIONARY HAS VALUE OBJECTS THAT CONTAIN A 'list' PROPERTY. 
       // HOW DO I PASS THIS CODE SNIPPET LINE IN AS PART OF A CLOSURE, WHICH USES 'withDictionaryValue' AND 'selfValue'? 

      } else { 
       // Simply write into this key - it doesn't yet contain values. 
       returnDictionary[key] = self[key] 
      } 
     } 

     return returnDictionary 

    } 

} 

回答

3

这个通用的合并不应该需要知道,这个特殊的字典里包含一个“列表值对象'财产。

相反,为了让你的函数访问字典的值list属性,你需要告诉这些值有list属性的编译器。

// your protocol that defines the list property 
protocol ListType { 
    var list : [AnyObject] { get set } 
} 

extension Dictionary where Value : ListType { 
    func merge(withDictionary: Dictionary) -> Dictionary { 
     var returnDictionary = withDictionary // make a copy of dictionary (a dictionary is a struct in Swift) 

     for (key, value) in self { // iterate through key value pairs 
      if let withDictionaryValue = returnDictionary[key] { // if value exists, merge the list properties 
       returnDictionary[key]!.list = value.list + withDictionaryValue.list 
      } else { 
       returnDictionary[key] = value 
      } 
     } 
     return returnDictionary 
    } 
} 

然后,您可以简单地符合您正在使用该协议直接值类型:您可以通过创建一个协议和约束扩展到只在具有值符合本协议的字典操作做到这一点,或通过扩展。

struct Foo : ListType { 
    var list: [AnyObject] 
} 

let d = ["foo" : Foo(list: ["foo", "bar", "baz"])] 
let d1 = ["foo" : Foo(list: ["qux", "blah", "blue"])] 

let r = d.merge(d1) // ["foo": Foo(list: [foo, bar, baz, qux, blah, blue])] 

如果你想要一个更通用的方法,你可以定义一个Mergable协议定义了一个方法,其中在贴合型可以做自己的组合逻辑。如果值符合协议,那么您将更改扩展来调用此方法,否则只需合并键值对。

protocol Mergable { 
    func merge(withOther:Self) -> Self 
} 

// if values are Mergable (and the key-value pairs exist in both dictionaries), then call their custom logic for merging 
extension Dictionary where Value : Mergable { 
    func merge(withDictionary: Dictionary) -> Dictionary { 
     var returnDictionary = withDictionary 
     for (key, value) in self { 
      if let withDictionaryValue = withDictionary[key] { 
       returnDictionary[key] = value.merge(withDictionaryValue) 
      } else { 
       returnDictionary[key] = value 
      } 
     } 
     return returnDictionary 
    } 
} 

// standard merging logic 
extension Dictionary { 
    func merge(withDictionary: Dictionary) -> Dictionary { 
     var returnDictionary = withDictionary 
     keys.forEach {returnDictionary[$0] = self[$0]} 
     return returnDictionary 
    } 
} 

然后,您可以符合你的价值类型与本协议,像这样:

// Foo's custom merging logic 
extension Foo : Mergable { 
    func merge(withOther: Foo) -> Foo { 
     var merged = self 

     // merge the list array 
     merged.list.appendContentsOf(withOther.list) 

     return merged 
    } 
} 

如果你的字典的价值观是Mergable,那么编译器会优先考虑做自定义组合逻辑的延伸,作为更多的类型特定签名是首选。


在回答你关于希望与封闭做到这一点评论,你可以添加一个闭合参数的merge功能将通过(通过使用inout)伪参考第一个值,随着第二个值,允许您在调用函数时改变第一个值。

extension Dictionary { 
    func merge(withDictionary: Dictionary, @noescape merge: (value: inout Value, withValue: Value) ->()) -> Dictionary { 
     var returnDictionary = withDictionary // make a copy of dictionary (a dictionary is a struct in Swift) 

     for (key, value) in self { // iterate through key value pairs 
      if let withDictionaryValue = returnDictionary[key] { 

       // create mutable copy of the value 
       var value = value 
       merge(value:&value, withValue:withDictionaryValue) // invoke closure to write merging changes to the value 
       returnDictionary[key] = value // assign value 

      } else { 
       returnDictionary[key] = value 
      } 
     } 
     return returnDictionary 
    } 
} 

// call merge with our own custom merging logic when a given key exists in both dictionaries 
let result = dictA.merge(dictB) {$0.list.appendContentsOf($1.list)} 

虽然这只是真正意义,如果你的组合逻辑在每次调用函数,我怀疑是不是自己在做什么都会改变。

如果您希望合并逻辑保持不变,那么使用协议可以更好地完成此操作。使用闭包时,每次要调用函数时都必须传入合并逻辑。使用协议,您只需定义一次该逻辑 - 并在需要时调用它。

+0

谢谢!是否有更多的功能编程方法呢?使用闭包?这就是我想要遵循的,而不是基于协议的方法。 (不想开始混合我的代码中的范例...) – Daniel

+0

@Daniel我已经更新了我的答案,显示了如何以这种方式进行操作,尽管IMO协议是更好的方法。使用闭包,每次调用函数时都必须传递自定义逻辑,这并不理想。协议允许你定义这个逻辑一次,并在需要时调用它。 – Hamish

+0

很好的答案,谢谢!一个问题 - 当你在函数式编程解决方案中调用merge()时,为什么你需要做“var returnValue = $ 0”? – Daniel