diese allgemeine Zusammenführung sollte nicht wissen müssen, dass diese besondere Wörterbuch Wertobjekte hat, die eine Liste enthalten ' Eigentum.
Im Gegenteil, für Ihre Funktion, um die list
Eigenschaft auf dem Wörterbuch der Werte zugreifen zu können, müssen Sie den Compiler sagen, dass die Werte haben eine list
Eigenschaft. Sie können dies tun, indem ein Protokoll und Einschränken der Erweiterung zu schaffen nur auf Wörterbücher zu arbeiten, die Werte haben, die diesem Protokoll entsprechen:
// 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
}
}
Anschließend können Sie die Werttypen einfach anpassen Sie dieses Protokoll verwenden entweder direkt oder durch eine Erweiterung.
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])]
Wenn Sie einen allgemeineren Ansatz wollen, könnten Sie ein Mergable
Protokoll definieren, das ein Verfahren definiert, wo die bestimmungsgemäße Art ihre eigene Verbindungslogik tun. Sie würden dann Ihre Erweiterung ändern, um diese Methode aufzurufen, wenn die Werte dem Protokoll entsprechen, andernfalls können Sie einfach die Schlüssel/Wert-Paare zusammenführen.
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
}
}
Sie könnten dann Ihren Wert Typ dieses Protokolls entsprechen in etwa 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
}
}
Wenn Werte Ihr Wörterbuch der Mergable
sind, dann wird der Compiler die Erweiterung favorisieren, die die benutzerdefinierte verschmelzenden Logik der Fall ist, wie die mehr typspezifische Signatur wird bevorzugt.
In Antwort auf Ihren Kommentar zu wollen, dies mit Schließungen zu tun, Sie könnten einen Verschluss Argument die merge
Funktion hinzufügen, die eine Pseudoreferenz auf den ersten Wert (durch Verwendung inout
) passieren, zusammen mit der zweite Wert, mit dem Sie den ersten Wert beim Aufrufen der Funktion ändern können.
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)}
Obwohl dies nur dann wirklich sinnvoll, wenn Ihre verschmelzenden Logik ändert sich mit jedem Aufruf der Funktion, die ich vermute, ist nicht das, was du tust.
Wenn Sie möchten, dass Ihre Merging-Logik konstant bleibt, dann ist dies besser mit Protokollen möglich. Bei Closures müssen Sie jedes Mal die Merging-Logik übergeben, wenn Sie die Funktion aufrufen möchten. Bei Protokollen definieren Sie diese Logik nur einmal - und sie wird bei Bedarf aufgerufen.
danke! Gibt es einen funktionaleren Programmieransatz? Schließungen verwenden? Dem möchte ich im Gegensatz zum protokollbasierten Ansatz folgen. (Ich möchte nicht anfangen, die Paradigmen in meinem Code zu mischen ...) – Daniel
@Daniel Ich habe meine Antwort aktualisiert, die zeigt, wie man es so machen kann, obwohl IMO-Protokolle eine bessere Möglichkeit sind, dies zu tun. Wenn Sie Closures verwenden, müssen Sie bei jedem Aufruf der Funktion Ihre benutzerdefinierte Logik übergeben, was nicht ideal ist. Protokolle ermöglichen es Ihnen, diese Logik einmal zu definieren und sie bei Bedarf aufzurufen. – Hamish
Große Antwort, danke! Eine Frage - warum müssen Sie "var returnValue = $ 0" ausführen, wenn Sie merge() in der funktionalen Programmierlösung aufrufen? – Daniel