swift - 合并两个字典的通用函数

标签 swift dictionary merge functional-programming

我想要一个通用函数来合并两个字典。它需要比 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

    }

}

最佳答案

This generic merge shouldn't need to know that this particular dictionary has value objects that contain a 'list' property.

相反,为了让您的函数访问字典值的 list 属性,您需要告诉编译器这些值具有 list 属性。您可以通过创建协议(protocol)并将扩展限制为仅对具有符合此协议(protocol)的值的字典进行操作来实现此目的:

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

然后,您可以直接或通过扩展使您使用的值类型符合此协议(protocol)。

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),该协议(protocol)定义了一种方法,其中符合类型可以执行自己的合并逻辑。然后,如果值符合协议(protocol),您可以更改扩展以调用此方法,否则只需合并键值对。

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

然后,您可以使您的值类型符合此协议(protocol),如下所示:

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

尽管只有当您每次调用该函数时合并逻辑都会发生变化时,这才真正有意义,但我怀疑这不是您正在做的事情。

如果您希望合并逻辑保持不变,那么最好使用协议(protocol)来完成。使用闭包,您每次想要调用函数时都必须传入合并逻辑。使用协议(protocol),您只需定义该逻辑一次 - 并在需要时调用它。

关于swift - 合并两个字典的通用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37741027/

相关文章:

ios - 如何在 NS 字典中设置类对象

python - 为什么从字典派生的类不显示包含键

java - findViewById 用于在包含的合并根元素内查看

svn - 颠覆 : Fail update when there are conflicts?

R merge() 不再按预期工作

swift - 在 Swift 中交换枚举值

ios - 如何在 swift 中创建像 tinder 这样的堆栈 View ?

java - 给定 Map 的不同 Collection View 的迭代顺序是否保证一致?

ios - 具有多个部分的 TableView

switch-statement - 优化 Swift 代码(switch case)