This generic merge shouldn't need to know that this particular dictionary has value objects that contain a 'list' property.
On the contrary, in order for your function to access the list
property on the dictionary's values, you need to tell the compiler that the values have a list
property. You can do this by creating a protocol and constraining the extension to only operate on dictionaries that have values that conform to this 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
}
}
You can then simply conform the value types you're using to this protocol either directly, or through an extension.
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])]
If you want a more general approach, you could define a Mergable
protocol that defines a method where the conforming type can do their own merging logic. You would then change your extension to call this method if the values conform to the protocol, else just merge the key-value pairs.
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
}
}
You could then conform your value type to this protocol like so:
// 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
}
}
If your dictionary's values are Mergable
, then the compiler will favour the extension that does the custom merging logic, as the more type specific signature is preferred.
In reply to your comment about wanting to do this with closures, you could add a closure argument to the merge
function that will pass a pseudo reference to the first value (through using inout
), along with the second value, allowing you to mutate the first value when you invoke the function.
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)}
Although this only really makes sense if your merging logic changes each time you call the function, which I suspect is not what you're doing.
If you want your merging logic to remain constant, then this better done with protocols. With closures you're going to have to pass in your merging logic every time you want to invoke the function. With protocols, you just define that logic once – and it's invoked when needed.