How to combine two Dictionary instances in Swift?

IosJsonXcodeDictionarySwift

Ios Problem Overview


How do I append one Dictionary to another Dictionary using Swift?

I am using the AlamoFire library to send a JSON to a REST server.

Dictionary 1

var dict1: [String: AnyObject] = [
    kFacebook: [
        kToken: token
    ]
]

Dictionary 2

var dict2: [String: AnyObject] = [
    kRequest: [
        kTargetUserId: userId
    ]
]

How do I combine the two dictionaries to make a new dictionary as shown below?

let parameters: [String: AnyObject] = [
    kFacebook: [
        kToken: token
    ],
    kRequest: [
        kTargetUserId: userId
    ]
]

I have tried dict1 += dict2 but got a compile error: >Binary operator '+=' cannot be applied to two '[String : AnyObject]' operands

Ios Solutions


Solution 1 - Ios

I love this approach:

dicFrom.forEach { (key, value) in dicTo[key] = value }

Swift 4 and 5

With Swift 4 Apple introduces a better approach to merge two dictionaries:

let dictionary = ["a": 1, "b": 2]
let newKeyValues = ["a": 3, "b": 4]

let keepingCurrent = dictionary.merging(newKeyValues) { (current, _) in current }
// ["b": 2, "a": 1]

let replacingCurrent = dictionary.merging(newKeyValues) { (_, new) in new }
// ["b": 4, "a": 3]

You have 2 options here (as with most functions operating on containers):

  • merge mutates an existing dictionary
  • merging returns a new dictionary

Solution 2 - Ios

var d1 = ["a": "b"]
var d2 = ["c": "e"]

extension Dictionary {
    mutating func merge(dict: [Key: Value]){
        for (k, v) in dict {
            updateValue(v, forKey: k)
        }
    }
}

d1.merge(d2)

Refer to the awesome Dollar & Cent project https://github.com/ankurp/Cent/blob/master/Sources/Dictionary.swift

Solution 3 - Ios

For Swift >= 2.2:
let parameters = dict1.reduce(dict2) { r, e in var r = r; r[e.0] = e.1; return r }

For Swift < 2.2:
let parameters = dict1.reduce(dict2) { (var r, e) in r[e.0] = e.1; return r }

Swift 4 has a new function: let parameters = dict1.reduce(into: dict2) { (r, e) in r[e.0] = e.1 }

It's really important to dig around the standard library: map, reduce, dropFirst, forEach etc. are staples of terse code. The functional bits are fun!

Solution 4 - Ios

We can merge dictionaries in a better way using merge keyword

var dictionary = ["a": 1, "b": 2]
/// Keeping existing value for key "a":
dictionary.merge(["a": 3, "c": 4]) { (current, _) in current }
 // ["b": 2, "a": 1, "c": 4]

Solution 5 - Ios

extension Dictionary {
    static func +=(lhs: inout Self, rhs: Self) {
        lhs.merge(rhs) { _ , new in new }
    }
    static func +=<S: Sequence>(lhs: inout Self, rhs: S) where S.Element == (Key, Value) {
        lhs.merge(rhs) { _ , new in new }
    }
}

var dic = ["test1": 1]
dic += ["test2": 2]
dic  // ["test1": 1, "test2": 2]
dic += [("test2", 3),("test3", 4)]
dic  // ["test3": 4, "test1": 1, "test2": 3]

Solution 6 - Ios

SequenceType.forEach (implemented by Dictionary) provides an elegant solution to add a dictionary's elements to another dictionary.

dic1.forEach { dic2[$0] = $1 }

For example

func testMergeDictionaries() {
    let dic1 = [1:"foo"]
    var dic2 = [2:"bar"]
    
    dic1.forEach { dic2[$0] = $1 }
    
    XCTAssertEqual(dic2[1], "foo")
}

Solution 7 - Ios

My needs were different, I wanted to merge and not clobber.

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

I was hoping for a simpler solution, but this is what I ended up with. The challenge was in hopping 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)")

Solution 8 - Ios

Try This Approach

    let dict1: [String: AnyObject] = ["kFacebook": ["kToken": "token"]]
    let dict2: [String: AnyObject] = ["kRequest": ["kTargetUserId": "userId"]]
    
    var combinedAttributes : NSMutableDictionary!
    
    combinedAttributes = NSMutableDictionary(dictionary: dict1)
    
    combinedAttributes.addEntriesFromDictionary(dict2)
    
    println(combinedAttributes)

It will Print Following :

{
kFacebook =     {
    kToken = token;
};
kRequest =     {
    kTargetUserId = userId;
};

}

Hope it helps !!

Solution 9 - Ios

You can use the below code to combine two dictionary instances in Swift:

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

Solution 10 - Ios

You are using let keyword to declare the dictionary so you can't make the changes to your dictionary as it is used to declare constant.

Change it to var keyword then it will work for you.

var dict1: [String: AnyObject] = [
            kFacebook: [
                kToken: token
            ]
        ]

var dict2: [String: AnyObject] = [
        kRequest: [
            kTargetUserId: userId
        ]
    ]

dict1 += dict2

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionThe NomadView Question on Stackoverflow
Solution 1 - IosblackjacxView Answer on Stackoverflow
Solution 2 - IosShuoView Answer on Stackoverflow
Solution 3 - IosempView Answer on Stackoverflow
Solution 4 - IosRaja JawaharView Answer on Stackoverflow
Solution 5 - IosLeo DabusView Answer on Stackoverflow
Solution 6 - IosrhookeView Answer on Stackoverflow
Solution 7 - IosChris ConoverView Answer on Stackoverflow
Solution 8 - IosWolverineView Answer on Stackoverflow
Solution 9 - IosPallaviView Answer on Stackoverflow
Solution 10 - IosMeenakshiView Answer on Stackoverflow