Les tableaux dans Swift prennent en charge l'opérateur + = pour ajouter le contenu d'un tableau à un autre. Y at-il un moyen facile de faire cela pour un dictionnaire?
par exemple:
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
var combinedDict = ... (some way of combining dict1 & dict2 without looping)
Vous pouvez définir l’opérateur +=
pour Dictionary
, par exemple,
func += <K, V> (left: inout [K:V], right: [K:V]) {
for (k, v) in right {
left[k] = v
}
}
Que diriez-vous
dict2.forEach { (k,v) in dict1[k] = v }
Cela ajoute toutes les clés et les valeurs de dict2 dans dict1.
Dans Swift 4, on devrait utiliser merging(_:uniquingKeysWith:)
:
let dictA = ["x" : 1, "y": 2, "z": 3]
let dictB = ["x" : 11, "y": 22, "w": 0]
let resultA = dictA.merging(dictB, uniquingKeysWith: { (first, _) in first })
let resultB = dictA.merging(dictB, uniquingKeysWith: { (_, last) in last })
print(resultA) // ["x": 1, "y": 2, "z": 3, "w": 0]
print(resultB) // ["x": 11, "y": 22, "z": 3, "w": 0]
Actuellement, en regardant référence de la bibliothèque Swift Standard pour Dictionnaire, il n’ya aucun moyen de mettre à jour facilement un dictionnaire avec un autre.
Vous pouvez écrire une extension pour le faire
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
extension Dictionary {
mutating func update(other:Dictionary) {
for (key,value) in other {
self.updateValue(value, forKey:key)
}
}
}
dict1.update(dict2)
// dict1 is now ["a" : "foo", "b" : "bar]
Swift 4 fournit merging(_:uniquingKeysWith:)
, donc pour votre cas:
let combinedDict = dict1.merging(dict2) { $1 }
La fermeture abrégée renvoie $1
, donc la valeur de dict2 sera utilisée en cas de conflit avec les clés.
Cela ne fait pas partie de la bibliothèque Swift, mais vous pouvez ajouter ce que vous voulez avec la surcharge d'opérateur, par exemple:
func + <K,V>(left: Dictionary<K,V>, right: Dictionary<K,V>)
-> Dictionary<K,V>
{
var map = Dictionary<K,V>()
for (k, v) in left {
map[k] = v
}
for (k, v) in right {
map[k] = v
}
return map
}
Cela surcharge l'opérateur +
pour les dictionnaires que vous pouvez maintenant utiliser pour ajouter des dictionnaires avec l'opérateur +
, par exemple:
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
var dict3 = dict1 + dict2 // ["a": "foo", "b": "bar"]
Swift 3:
extension Dictionary {
mutating func merge(with dictionary: Dictionary) {
dictionary.forEach { updateValue($1, forKey: $0) }
}
func merged(with dictionary: Dictionary) -> Dictionary {
var dict = self
dict.merge(with: dictionary)
return dict
}
}
let a = ["a":"b"]
let b = ["1":"2"]
let c = a.merged(with: b)
print(c) //["a": "b", "1": "2"]
Swift 2.0
extension Dictionary {
mutating func unionInPlace(dictionary: Dictionary) {
dictionary.forEach { self.updateValue($1, forKey: $0) }
}
func union(var dictionary: Dictionary) -> Dictionary {
dictionary.unionInPlace(self)
return dictionary
}
}
Je préfère combiner/unir des dictionnaires immuables avec l'opérateur +
. Je l'ai donc implémenté comme suit:
// Swift 2
func + <K,V> (left: Dictionary<K,V>, right: Dictionary<K,V>?) -> Dictionary<K,V> {
guard let right = right else { return left }
return left.reduce(right) {
var new = $0 as [K:V]
new.updateValue($1.1, forKey: $1.0)
return new
}
}
let moreAttributes: [String:AnyObject] = ["Function":"authenticate"]
let attributes: [String:AnyObject] = ["File":"Auth.Swift"]
attributes + moreAttributes + nil //["Function": "authenticate", "File": "Auth.Swift"]
attributes + moreAttributes //["Function": "authenticate", "File": "Auth.Swift"]
attributes + nil //["File": "Auth.Swift"]
// Swift 2
func += <K,V> (inout left: Dictionary<K,V>, right: Dictionary<K,V>?) {
guard let right = right else { return }
right.forEach { key, value in
left.updateValue(value, forKey: key)
}
}
let moreAttributes: [String:AnyObject] = ["Function":"authenticate"]
var attributes: [String:AnyObject] = ["File":"Auth.Swift"]
attributes += nil //["File": "Auth.Swift"]
attributes += moreAttributes //["File": "Auth.Swift", "Function": "authenticate"]
Pas besoin d'avoir des extensions de dictionnaire maintenant. Le dictionnaire Swift (Xcode 9.0+) possède une fonctionnalité pour cela. Regardez ici . Ci-dessous, voici un exemple d'utilisation
var oldDictionary = ["a": 1, "b": 2]
var newDictionary = ["a": 10000, "b": 10000, "c": 4]
oldDictionary.merge(newDictionary) { (oldValue, newValue) -> Int in
// This closure return what value to consider if repeated keys are found
return newValue
}
print(oldDictionary) // Prints ["b": 10000, "a": 10000, "c": 4]
Tu peux essayer ça
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
var temp = NSMutableDictionary(dictionary: dict1);
temp.addEntriesFromDictionary(dict2)
Une variante plus lisible utilisant une extension.
extension Dictionary {
func merge(dict: Dictionary<Key,Value>) -> Dictionary<Key,Value> {
var mutableCopy = self
for (key, value) in dict {
// If both dictionaries have a value for same key, the value of the other dictionary is used.
mutableCopy[key] = value
}
return mutableCopy
}
}
Vous pouvez également utiliser réduire pour les fusionner. Essayez ceci dans la cour
let d1 = ["a":"foo","b":"bar"]
let d2 = ["c":"car","d":"door"]
let d3 = d1.reduce(d2) { (var d, p) in
d[p.0] = p.1
return d
}
Je recommande le SwifterSwift Library . Cependant, si vous ne voulez pas utiliser toute la bibliothèque et tous ses ajouts, vous pouvez simplement utiliser leur extension de Dictionary:
public extension Dictionary {
public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) {
rhs.forEach({ lhs[$0] = $1})
}
}
Vous pouvez parcourir les combinaisons de valeurs de clé dans la valeur que vous souhaitez fusionner et les ajouter via la méthode updateValue (forKey :):
dictionaryTwo.forEach {
dictionaryOne.updateValue($1, forKey: $0)
}
Maintenant, toutes les valeurs de dictionaryTwo ont été ajoutées à dictionaryOne.
Identique à la réponse de @ farhadf mais adoptée pour Swift 3:
let sourceDict1 = [1: "one", 2: "two"]
let sourceDict2 = [3: "three", 4: "four"]
let result = sourceDict1.reduce(sourceDict2) { (partialResult , pair) in
var partialResult = partialResult //without this line we could not modify the dictionary
partialResult[pair.0] = pair.1
return partialResult
}
Swift 3, extension du dictionnaire:
public extension Dictionary {
public static func +=(lhs: inout Dictionary, rhs: Dictionary) {
for (k, v) in rhs {
lhs[k] = v
}
}
}
Vous pouvez ajouter une Dictionary
extension comme ceci:
extension Dictionary {
func mergedWith(otherDictionary: [Key: Value]) -> [Key: Value] {
var mergedDict: [Key: Value] = [:]
[self, otherDictionary].forEach { dict in
for (key, value) in dict {
mergedDict[key] = value
}
}
return mergedDict
}
}
Alors utilisation est aussi simple que :
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
var combinedDict = dict1.mergedWith(dict2)
// => ["a": "foo", "b": "bar"]
Si vous préférez un framework qui inclut également des fonctionnalités plus pratiques , passez à la caisse HandySwift. importez-le dans votre projet et vous pouvez utiliser le code ci-dessus sans ajouter d’extension au projet vous-même.
Quelques surcharges encore plus simples pour Swift 4:
extension Dictionary {
static func += (lhs: inout [Key:Value], rhs: [Key:Value]) {
lhs.merge(rhs){$1}
}
static func + (lhs: [Key:Value], rhs: [Key:Value]) -> [Key:Value] {
return lhs.merging(rhs){$1}
}
}
Il n'y a plus besoin d'extension ni de fonction supplémentaire. Vous pouvez écrire comme ça:
firstDictionary.merge(secondDictionary) { (value1, value2) -> AnyObject in
return object2 // what you want to return if keys same.
}
import Foundation
let x = ["a":1]
let y = ["b":2]
let out = NSMutableDictionary(dictionary: x)
out.addEntriesFromDictionary(y)
Le résultat est un dictionnaire NSMutableDictionary et non pas un dictionnaire typé Swift, mais la syntaxe à utiliser est la même (out["a"] == 1
dans ce cas), de sorte que vous n'auriez un problème que si vous utilisez une troisième Code -party qui attend un dictionnaire Swift ou qui a vraiment besoin de la vérification du type.
La réponse courte ici est que vous devez réellement faire une boucle. Même si vous ne le saisissez pas explicitement, c'est ce que fera la méthode que vous appelez (addEntriesFromDictionary: here). Si vous ne comprenez pas très bien pourquoi, vous devriez envisager de fusionner les nœuds d'extrémité de deux arbres B.
Si vous avez réellement besoin d'un type de dictionnaire natif Swift en retour, je vous conseillerais:
let x = ["a":1]
let y = ["b":2]
var out = x
for (k, v) in y {
out[k] = v
}
L'inconvénient de cette approche est que l'index du dictionnaire peut être reconstruit plusieurs fois dans la boucle, ce qui est pratiquement 10 fois plus lent que dans l'approche NSMutableDictionary.
Vous pouvez utiliser la fonction bridgeToObjectiveC () pour transformer le dictionnaire en NSDictionary.
Sera comme ce qui suit:
var dict1 = ["a":"Foo"]
var dict2 = ["b":"Boo"]
var combinedDict = dict1.bridgeToObjectiveC()
var mutiDict1 : NSMutableDictionary! = combinedDict.mutableCopy() as NSMutableDictionary
var combineDict2 = dict2.bridgeToObjectiveC()
var combine = mutiDict1.addEntriesFromDictionary(combineDict2)
Ensuite, vous pouvez convertir le NSDictionary (combine) en arrière ou faire ce que vous voulez.
Toutes ces réponses sont compliquées. Voici ma solution pour Swift 2.2:
//get first dictionnary
let finalDictionnary : NSMutableDictionary = self.getBasicDict()
//cast second dictionnary as [NSObject : AnyObject]
let secondDictionnary : [NSObject : AnyObject] = self.getOtherDict() as [NSObject : AnyObject]
//merge dictionnary into the first one
finalDictionnary.addEntriesFromDictionary(secondDictionnary)
Voici une belle extension que j'ai écrite ...
extension Dictionary where Value: Any {
public func mergeOnto(target: [Key: Value]?) -> [Key: Value] {
guard let target = target else { return self }
return self.merging(target) { current, _ in current }
}
}
utiliser:
var dict1 = ["cat": 5, "dog": 6]
var dict2 = ["dog": 9, "rodent": 10]
dict1 = dict1.mergeOnto(target: dict2)
Ensuite, dict1 sera modifié pour
["cat": 5, "dog": 6, "rodent": 10]
Mes besoins étaient différents, je devais fusionner des ensembles de données imbriqués incomplets sans faire de bruit.
merging:
["b": [1, 2], "s": Set([5, 6]), "a": 1, "d": ["x": 2]]
with
["b": [3, 4], "s": Set([6, 7]), "a": 2, "d": ["y": 4]]
yields:
["b": [1, 2, 3, 4], "s": Set([5, 6, 7]), "a": 2, "d": ["y": 4, "x": 2]]
C'était plus difficile que je ne l'aurais voulu. Le défi consistait à mapper du typage dynamique au typage statique, et j’ai utilisé des protocoles pour résoudre ce problème.
Il est également intéressant de noter que lorsque vous utilisez la syntaxe littérale du dictionnaire, vous obtenez les types de base, qui ne récupèrent pas les extensions de protocole. J'ai abandonné mes efforts pour les soutenir car je ne trouvais pas facile de valider l'uniformité des éléments de la collection.
import UIKit
private protocol Mergable {
func mergeWithSame<T>(right: T) -> T?
}
public extension Dictionary {
/**
Merge Dictionaries
- Parameter left: Dictionary to update
- Parameter right: Source dictionary with values to be merged
- Returns: Merged dictionay
*/
func merge(right:Dictionary) -> Dictionary {
var merged = self
for (k, rv) in right {
// case of existing left value
if let lv = self[k] {
if let lv = lv as? Mergable where lv.dynamicType == rv.dynamicType {
let m = lv.mergeWithSame(rv)
merged[k] = m
}
else if lv is Mergable {
assert(false, "Expected common type for matching keys!")
}
else if !(lv is Mergable), let _ = lv as? NSArray {
assert(false, "Dictionary literals use incompatible Foundation Types")
}
else if !(lv is Mergable), let _ = lv as? NSDictionary {
assert(false, "Dictionary literals use incompatible Foundation Types")
}
else {
merged[k] = rv
}
}
// case of no existing value
else {
merged[k] = rv
}
}
return merged
}
}
extension Array: Mergable {
func mergeWithSame<T>(right: T) -> T? {
if let right = right as? Array {
return (self + right) as? T
}
assert(false)
return nil
}
}
extension Dictionary: Mergable {
func mergeWithSame<T>(right: T) -> T? {
if let right = right as? Dictionary {
return self.merge(right) as? T
}
assert(false)
return nil
}
}
extension Set: Mergable {
func mergeWithSame<T>(right: T) -> T? {
if let right = right as? Set {
return self.union(right) as? T
}
assert(false)
return nil
}
}
var dsa12 = Dictionary<String, Any>()
dsa12["a"] = 1
dsa12["b"] = [1, 2]
dsa12["s"] = Set([5, 6])
dsa12["d"] = ["c":5, "x": 2]
var dsa34 = Dictionary<String, Any>()
dsa34["a"] = 2
dsa34["b"] = [3, 4]
dsa34["s"] = Set([6, 7])
dsa34["d"] = ["c":-5, "y": 4]
//let dsa2 = ["a": 1, "b":a34]
let mdsa3 = dsa12.merge(dsa34)
print("merging:\n\t\(dsa12)\nwith\n\t\(dsa34) \nyields: \n\t\(mdsa3)")
Swift 2.2
func + <K,V>(left: [K : V], right: [K : V]) -> [K : V] {
var result = [K:V]()
for (key,value) in left {
result[key] = value
}
for (key,value) in right {
result[key] = value
}
return result
}
Je voudrais juste utiliser la bibliothèque Dollar .
https://github.com/ankurp/Dollar/#merge---merge-1
Fusionne tous les dictionnaires et ce dernier dictionnaire remplace la valeur à une clé donnée
let dict: Dictionary<String, Int> = ["Dog": 1, "Cat": 2]
let dict2: Dictionary<String, Int> = ["Cow": 3]
let dict3: Dictionary<String, Int> = ["Sheep": 4]
$.merge(dict, dict2, dict3)
=> ["Dog": 1, "Cat": 2, "Cow": 3, "Sheep": 4]