web-dev-qa-db-fra.com

Comment ajouter un dictionnaire d'éléments dans un autre dictionnaire

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)
164
rustyshelf

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

Que diriez-vous

dict2.forEach { (k,v) in dict1[k] = v }

Cela ajoute toutes les clés et les valeurs de dict2 dans dict1.

87
jasongregori

Dans Swift 4, on devrait utiliser merging(_:uniquingKeysWith:) :

Exemple:

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]
83
Vin Gazoil

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]
76
Rod

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.

64
samwize

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"]
31
mythz

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"]
27
Pavel Sharanda

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

Immuable

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"]

Mutable

// 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"]
12
ricardopereira

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]
12
Vinayak Parmar

Tu peux essayer ça

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var temp = NSMutableDictionary(dictionary: dict1);
temp.addEntriesFromDictionary(dict2)
12
Uttam Kumar

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

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

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:

Swift 3+

public extension Dictionary {
    public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) {
        rhs.forEach({ lhs[$0] = $1})
    }
}
7
Justin Oroz

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.

5
LeonS

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
}
4
Dmitry Klochkov

Swift 3, extension du dictionnaire:

public extension Dictionary {

    public static func +=(lhs: inout Dictionary, rhs: Dictionary) {
        for (k, v) in rhs {
            lhs[k] = v
        }
    }

}
4
aaannjjaa

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.

3
Dschee

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}
    }
}
3
John Montgomery

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.
    }
1
Burak Dizlek
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.

1
Jim Driscoll

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.

1
Anton

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) 
1
Kevin ABRIOUX

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]

0
Brian J. Roberts

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)")
0
Chris Conover

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

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]
0
Andy