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