How do you add a Dictionary of items into another Dictionary
Asked Answered
T

28

198

Arrays in Swift support the += operator to add the contents of one Array to another. Is there an easy way to do that for a dictionary?

eg:

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

var combinedDict = ... (some way of combining dict1 & dict2 without looping)
Torn answered 5/6, 2014 at 4:55 Comment(2)
developer.apple.com/documentation/swift/dictionary/…Moloch
fromDict.forEach {intoDict[$0] = $1}Curiel
S
183

You can define += operator for Dictionary, e.g.,

func += <K, V> (left: inout [K:V], right: [K:V]) { 
    for (k, v) in right { 
        left[k] = v
    } 
}
Sibilate answered 5/6, 2014 at 5:14 Comment(13)
Oh man, I struggled so much with finding the proper generic declaration for this, I tried everything except this. But you can drop the @assignment and return, you're already mutating left. Edit: actually, even though I get no errors, I think @assignment should stay.Czechoslovak
Where do I add this in my code or project in order to use it? I tried adding a separate swift file and I also tried adding it in the class file where I'm trying to use it.Tonsillectomy
More syntax sugar: func +=<K, V> (inout left: [K : V], right: [K : V]) { for (k, v) in right { left[k] = v } }Manufacturer
I don't get it. How should this method be called? func +=<K, V> (inout left: [K: V], right: [K: V]){ for (k, v) in right { left[k] = v } } var test = ["1": "a"] += ["2": "b"] causes: Binary operator '+=' cannot be applied to two [String : String] operands.Spikenard
@JeanLebrument you can no more write var test = ["1": "a"] += ["2": "b"] than var test = 2 += 3. You would write var test = ["1": "a"] + ["2": "b"], and for this to work we would need to write another generic function, which would look like this func +<K, V> (left: [K : V], right: [K : V]) -> [K : V]{var new = [K : V](); for (k, v) in right { new[k] = v }; return new }Delvalle
@DavidRobertson the following code works in Xcode 7.2: func +=<K, V> (inout left: [K : V], right: [K : V]) { for (k, v) in right { left[k] = v } } and given a scenario where var aDict = ["1": "a"] and let bDict = ["2": "b"] we can write aDict += bDictDelvalle
@JeanLebrument oops, generic function should be written func +<K, V> (left: [K : V], right: [K : V]) -> [K : V]{var new = [K : V](); for (k, v) in left { new[k] = v }; for (k, v) in right { new[k] = v }; return new }Delvalle
@animal_chin Because we have to implement half of the language ourselves? Yes. Impressed. Don't get me wrong I love operator overloading. I just don't love having to use it for basic features that should be built in.Torrens
@devios Haha then make it a pull request to the Swift repo :D Since evidently Apple can't be arsedPersonalism
Pulling straight from the SwifterSwift Library: public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) { rhs.forEach({ lhs[$0] = $1}) }Dani
operators should be static :]Oreopithecus
That doesn't work for nested dictionaries. imagine this: ["foo", ["foo": 1]] and ["foo": ["bar": 2]]Punnet
@Personalism They likely wouldn't accept an overload of +=. They decided to forego it for numerics I can't imagine them supporting it for dictionaries. They would probably accept an overload of + but it doesn't necessarily handle merge policies.Knapsack
A
134

In Swift 4, one should use merging(_:uniquingKeysWith:):

Example:

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]
Apomorphine answered 6/12, 2017 at 18:31 Comment(2)
//mutable: var dictA = ["x" : 1, "y": 2, "z": 3] var dictB = ["x" : 11, "y": 22, "w": 0] dictA.merge(dictB, uniquingKeysWith: { (first, _) in first }) print(dictA) // ["x": 1, "y": 2, "z": 3, "w": 0]Aftmost
The second example shown in this answer is the equivalent of [NSMutableDictionary addEntriesFromDictionary:].Skive
E
120

Swift 4 provides merging(_:uniquingKeysWith:), so for your case:

let combinedDict = dict1.merging(dict2) { $1 }

The shorthand closure returns $1, therefore dict2's value will be used when there is a conflict with the keys.

Elohist answered 25/5, 2018 at 14:58 Comment(2)
Just wanted to point out that this is the most concise and closest I have found to what the Apple documentation states for - (void)addEntriesFromDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary;. With regards to what to do with duplicates, it states that: "If both dictionaries contain the same key, the receiving dictionary’s previous value object for that key is sent a release message, and the new value object takes its place.", so in the Swift version, or in merge(_:uniquingKeysWith:), returning the second value, $1, is the same as what addEntriesFromDictionary does.Francinafrancine
you've got .merging(dict2) { Double.random(in: 0...1) == 0 ? $0 : $1 } for those cases where you're just not sure. #jokeExfoliation
S
103

How about

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

That adds all of dict2's keys and values into dict1.

Sacristy answered 21/9, 2016 at 21:44 Comment(2)
Nice solution. Slightly shorter: dict2.forEach { dict1[$0] = $1 }Cool
This a great solution, but for Swift 4, you will most likely get an error stating Closure tuple parameter '(key: _, value: _)' does not support destructuring (at least at the time of this writing). One would need to restructure the closure according to [this stackoverflow answer] (#44946467):Hopper
W
81

Currently, looking at the Swift Standard Library Reference for Dictionary, there is no way to easy update a dictionary with another one.

You can write an extension to do it

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]
Wherefrom answered 5/6, 2014 at 5:1 Comment(1)
This is a great use of extension for the Dictionary!Reimport
G
33

It's not built into the Swift library but you can add what you want with operator overloading, e.g:

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
}

This overloads the + operator for Dictionaries which you can now use to add dictionaries with the + operator, e.g:

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

var dict3 = dict1 + dict2 // ["a": "foo", "b": "bar"]
Grimy answered 5/6, 2014 at 5:8 Comment(7)
You could also do it for += to update in place a dict (as per the op question).Wherefrom
You can do away with map and drop the first for (k, v)... loop if you declare the left parameter as var and then just copy values from right into it.Straightedge
@NateCook that would mutate the Dictionary which is not expected behavior for + infix operator.Grimy
Thanks for that. Your answer was probably more accurate to the sample code I posted, while the other one was more what I wanted based on my question. My bad, anyway gave you both an upvote ;)Torn
@Grimy It isn't really mutating, since the + operator overload isn't a method of either Dictionary, it's a simple function. The changes you make to a variable left parameter wouldn't be visible outside the function.Straightedge
In case someone else comes across this post and wants both + and +=, note that in the version of Swift included with XCode 6.1.1 does not allow you to include @infix before the method definition.Halfblood
Using + is a bad idea since the usual assumption would be that the operator is commutative, which it is most certainly not here.Bill
S
28

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"]
Supercharger answered 24/9, 2016 at 17:56 Comment(1)
slightly better func merged(with dictionary: Dictionary<Key,Value>) -> Dictionary<Key,Value> { var copy = self dictionary.forEach { copy.updateValue($1, forKey: $0) } return copy }Rojas
P
16

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
    }
}
Parker answered 27/11, 2015 at 12:13 Comment(4)
can't call a mutating function from a non-mutating function like thatJacquejacquelin
The union function has the value passed into it be a var, meaning the copied dictionary can be mutated. It's a bit cleaner than func union(dictionary: Dictionary) -> Dictionary { var dict2 = dictionary; dict2.unionInPlace(self); return dict2 }, if only by one line.Epicurus
var params are deprecated and will be removed in Swift 3. The preferred way of doing this is now to declare a var in the body: var dictionary = dictionary. From here: github.com/apple/swift-evolution/blob/master/proposals/…Githens
To make things more type-safe, add <Key, Value> to those Dictionarys.Bill
D
15

No need to have any dictionary extensions now. Swift(Xcode 9.0+) dictionary has got a functionality for this. Have a look here. Below here is an example on how to use it

  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]
Denunciation answered 3/10, 2017 at 10:54 Comment(1)
I'm adding a functional style for the above example: oldDictionary.merge(newDictionary) { $1 }Nieves
S
13

Immutable

I prefer to combine/unite immutable dictionaries with + operator so I implemented it like:

// 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"]
Shortchange answered 30/12, 2015 at 10:20 Comment(3)
I don't understand why this isn't built into swift by default?Genip
are you intending values from left to override the right in your "Immutable" solution? I think you mean to have right.reduce(left), at least that is the expected behavior imo (and it's the behavior of your second example) - ie. ["A":1] + ["A":2] should output ["A":2]Precast
The output corresponds to the code. I want the initial value to be the right side, like it is right now.Shortchange
D
11

You can try this

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

var temp = NSMutableDictionary(dictionary: dict1);
temp.addEntriesFromDictionary(dict2)
Dickens answered 22/12, 2014 at 13:38 Comment(0)
T
11

A more readable variant using an 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
    }    
}
Tiffanietiffanle answered 2/2, 2016 at 18:2 Comment(1)
very nice and clean solution!Expound
V
10

You can also use reduce to merge them. Try this in the playground

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
}
Valente answered 13/10, 2015 at 15:50 Comment(6)
This looks interesting, but what are d and p?Imposing
d is the persisting result of each iteration of the reduce block and p is the element of the collection that is being reduced.Valente
this seems to crash in swift 3.0 betaBoyd
var parameters are deprecated in swift 3Idioblast
This is my favorite solution out of the ones mentioned here. Filter/map/reduce wins again for great concise solutions.Enjoy
An answer below updates this for Swift 3: https://mcmap.net/q/127589/-how-do-you-add-a-dictionary-of-items-into-another-dictionaryFayre
S
10

Some even more streamlined overloads for 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}
    }
}
Statute answered 14/12, 2018 at 22:52 Comment(0)
D
7

I recommend the SwifterSwift Library. However, if you don't want to use the entire library and all its great additions you can just make use of their extension of Dictionary:

Swift 3+

public extension Dictionary {
    public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) {
        rhs.forEach({ lhs[$0] = $1})
    }
}
Dani answered 10/6, 2017 at 5:38 Comment(1)
Actually, SE-110 has been reverted, so the Swift 4 version should be the same as the Swift 3 version.Isthmian
A
7

There is no need extension or any extra func anymore. You can write like that :

firstDictionary.merge(secondDictionary) { (value1, value2) -> AnyObject in
        return object2 // what you want to return if keys same.
    }
Adar answered 31/7, 2019 at 8:44 Comment(1)
or indeed firstDictionary.merge(secondDictionary) { (current, _) in current } to keep the first value and firstDictionary.merge(secondDictionary) { (_, new) in new } to keep the second value if the keys are the same.Deprive
H
5

You can iterate over the Key Value combinations ob the value you want to merge and add them via the updateValue(forKey:) method:

dictionaryTwo.forEach {
    dictionaryOne.updateValue($1, forKey: $0)
}

Now all values of dictionaryTwo got added to dictionaryOne.

Hufuf answered 30/1, 2017 at 14:22 Comment(0)
I
4

The same as @farhadf's answer but adopted for 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
}
Idioblast answered 27/10, 2016 at 7:30 Comment(0)
L
4

Swift 3, dictionary extension:

public extension Dictionary {

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

}
Lymphocyte answered 24/8, 2017 at 8:6 Comment(0)
C
4

You can use,

func addAll(from: [String: Any], into: [String: Any]){
    from.forEach {into[$0] = $1}
}
Curiel answered 7/11, 2019 at 12:26 Comment(0)
B
3

You can add a Dictionary extension like this:

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

Then usage is as simple as the following:

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

var combinedDict = dict1.mergedWith(dict2)
// => ["a": "foo", "b": "bar"]

If you prefer a framework that also includes some more handy features then checkout HandySwift. Just import it to your project and you can use the above code without adding any extensions to the project yourself.

Backstage answered 30/6, 2016 at 17:40 Comment(2)
Installing a library to use a single function is bad practiceKuska
@HackaZach: I just updated my answer to include the appropriate part of the framework to prevent inclusion of the entire library if only this little part is needed. I'm keeping the hint to the framework for people who want to use multiple of it's features. I hope this helps keeping good practice!Backstage
H
1

You can use the bridgeToObjectiveC() function to make the dictionary a NSDictionary.

Will be like the following:

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)

Then you can convert the NSDictionary(combine) back or do whatever.

Holytide answered 5/6, 2014 at 5:18 Comment(3)
Sorry what do you mean exactly?Holytide
Just a preference. Seems convoluted to bridge between languages. Better to stick within the confines of a single language, while simultaneously let obj-c die faster.Declarative
Yeah, I posted this answer literally on the day Swift announced....... So there was a reasonHolytide
K
1

All of these responses are complicated. This is my solution for 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) 
Kathe answered 10/5, 2016 at 9:2 Comment(1)
This only works on NSMutableDictionary and not native Swift dictionaries, unfortunately. I wish this would be added to Swift natively.Spin
C
1
import Foundation

let x = ["a":1]
let y = ["b":2]

let out = NSMutableDictionary(dictionary: x)
out.addEntriesFromDictionary(y)

The result is an NSMutableDictionary not a Swift typed dictionary, but the syntax to use it is the same (out["a"] == 1 in this case) so you'd only have a problem if you're using third-party code which expects a Swift dictionary, or really need the type checking.

The short answer here is that you actually do have to loop. Even if you're not entering it explicitly, that's what the method you're calling (addEntriesFromDictionary: here) will do. I'd suggest if you're a bit unclear on why that would be the case you should consider how you would merge the leaf nodes of two B-trees.

If you really actually need a Swift native dictionary type in return, I'd suggest:

let x = ["a":1]
let y = ["b":2]

var out = x
for (k, v) in y {
    out[k] = v
}

The downside of this approach is that the dictionary index - however it's done - may be rebuilt several times in the loop, so in practice this is about 10x slower than the NSMutableDictionary approach.

Cornerwise answered 12/5, 2016 at 18:42 Comment(0)
D
0

My needs were different, I needed to merge incomplete nested data sets without clobbering.

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

This was harder than I wanted it to be. The challenge was in mapping from dynamic typing to static typing, and I used protocols to solve this.

Also worthy of note is that when you use the dictionary literal syntax, you actually get the foundation types, which do not pick up the protocol extensions. I aborted my efforts to support those as I couldn't find an easy to to validate the uniformity of the collection elements.

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)")
Dauphin answered 23/4, 2016 at 0:41 Comment(0)
M
0

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
}
Mescal answered 4/8, 2016 at 13:10 Comment(1)
if you put this you can remove first loop: ` var result = left`Gemina
D
0

I would just use the Dollar library.

https://github.com/ankurp/Dollar/#merge---merge-1

Merges all of the dictionaries together and the latter dictionary overrides the value at a given key

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]
Dunstable answered 4/11, 2016 at 20:58 Comment(1)
jQuery is back yay!Callaghan
C
0

Here is a nice extension I wrote...

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

to use:

var dict1 = ["cat": 5, "dog": 6]
var dict2 = ["dog": 9, "rodent": 10]

dict1 = dict1.mergeOnto(target: dict2)

Then, dict1 will be modified to

["cat": 5, "dog": 6, "rodent": 10]

Consociate answered 26/2, 2019 at 20:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.