How can I combine two Dictionary instances in Swift?
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
How do you add a Dictionary of items into another Dictionary
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
}
}
How to add another dictionary entry to an existing dictionary to form a new dictionary (i.e. not append)
There is a built-in merging(_:uniquingKeysWith:)
function on Dictionary
that does exactly what you need.
let dictionary = [1:1]
let otherDictionary = [1:2, 2:3]
// This will take the values from `otherDictionary` if the same key exists in both
// You can use `$0` if you want to take the value from `dictionary` instead
let mergedDict = dictionary.merging(otherDictionary, uniquingKeysWith: { $1 })
If you want, you can easily define a +
operator for Dictionary
that uses the above function.
extension Dictionary {
static func + (lhs: Self, rhs: Self) -> Self {
lhs.merging(rhs, uniquingKeysWith: { $1 })
}
}
let addedDict = dictionary + otherDictionary
How do I write a generic Swift extension to deep merge two dictionaries?
Note that the error is on the assignment to result[key]
, not on the function call itself:
let merged: [Key:Value] = existing.deepMerged(with: value) //works fine
result[key] = merged //error
The compiler knows that:
result
is a[Key: Value]
other
is a[Key: Value]
key
is aKey
value
is aValue
merged
is a[Key:Value]
result
has asubscript(key: Key) -> Value? { get set }
(i.e. a writable subscript of typeValue?
which accepts aKey
)
But it does not know that [Key:Value]
is a Value
, so it does not know that merged
can be passed to the subscript of result
. Since value
is a Value
and value
is also [Key: Value]
, we know that [Key: Value]
must be a Value
, but the compiler does not know that.
A solution is to cast merged
to a Value
:
extension Dictionary {
public func deepMerged(with other: [Key: Value]) -> [Key: Value]
{
var result: [Key: Value] = self
for (key, value) in other {
if let value = value as? [Key: Value],
let existing = result[key] as? [Key: Value],
let merged = existing.deepMerged(with: value) as? Value
{
result[key] = merged
} else {
result[key] = value
}
}
return result
}
}
Merging Dictionaries with multiple Types, or Any / AnyObject Type
You don't actually need to (and you can't) specify the Any
type in the function declaration - you need to have the subtypes of the newValues
dictionary match the dictionary it's extending:
extension Dictionary {
mutating func extend(newVals: Dictionary) {
for (key,value) in newVals {
self.updateValue(value, forKey:key)
}
}
}
var dict = ["one": 1, "two": 2, "hello": "goodbye"]
dict.extend(["three": 3])
dict.extend(["foo": "bar"])
Key
and Value
are type aliases within the Dictionary
type that map to the specific key- and value-types of a particular instance.
Comparing two [String: Any] dictionaries in Swift 4
Details
- Xcode Version 10.3 (10G8), Swift 5
Solution
func areEqual (_ left: Any, _ right: Any) -> Bool {
if type(of: left) == type(of: right) &&
String(describing: left) == String(describing: right) { return true }
if let left = left as? [Any], let right = right as? [Any] { return left == right }
if let left = left as? [AnyHashable: Any], let right = right as? [AnyHashable: Any] { return left == right }
return false
}
extension Array where Element: Any {
static func != (left: [Element], right: [Element]) -> Bool { return !(left == right) }
static func == (left: [Element], right: [Element]) -> Bool {
if left.count != right.count { return false }
var right = right
loop: for leftValue in left {
for (rightIndex, rightValue) in right.enumerated() where areEqual(leftValue, rightValue) {
right.remove(at: rightIndex)
continue loop
}
return false
}
return true
}
}
extension Dictionary where Value: Any {
static func != (left: [Key : Value], right: [Key : Value]) -> Bool { return !(left == right) }
static func == (left: [Key : Value], right: [Key : Value]) -> Bool {
if left.count != right.count { return false }
for element in left {
guard let rightValue = right[element.key],
areEqual(rightValue, element.value) else { return false }
}
return true
}
}
Usage
let comparisonResult = ["key1": 1, 2: "Value2"] == ["key1": ["key2":2]] // false
print("!!!! \(comparisonResult)")
Some tests
func test(dict1: [AnyHashable : Any], dict2: [AnyHashable : Any]) {
print("========================")
print("dict1: \(dict1)")
print("dict2: \(dict2)")
print("are\(dict1 == dict2 ? " " : " not ")equal")
}
test(dict1: ["key1": 1, 2: "Value2"],
dict2: ["key1": 1, 2: "Value2"])
test(dict1: ["key1": 1, 2: "Value2"],
dict2: ["key1": 1])
test(dict1: [2: "Value2"],
dict2: ["key1": 1])
test(dict1: ["1": 1],
dict2: [1: 1])
test(dict1: [1: 2],
dict2: [1: 3])
test(dict1: ["key1": [1,2,3,4]],
dict2: ["key1": [1,2,3,4]])
test(dict1: ["key1": [1,2,3,4]],
dict2: ["key1": [2,1,3,4]])
test(dict1: ["key1": [1,2,3,4]],
dict2: ["key1": [2,1,3]])
test(dict1: ["key1": [1,2,3,4]],
dict2: ["key1": [1,2,3,"4"]])
test(dict1: ["key1": ["key2":2]],
dict2: ["key1": ["key2":2]])
test(dict1: ["key1": ["key2":2]],
dict2: ["key1": ["key2":3]])
test(dict1: ["key1": ["key2":2]],
dict2: ["key1": ["key2":3]])
Tests results
========================
dict1: [AnyHashable("key1"): 1, AnyHashable(2): "Value2"]
dict2: [AnyHashable("key1"): 1, AnyHashable(2): "Value2"]
are equal
========================
dict1: [AnyHashable("key1"): 1, AnyHashable(2): "Value2"]
dict2: [AnyHashable("key1"): 1]
are not equal
========================
dict1: [AnyHashable(2): "Value2"]
dict2: [AnyHashable("key1"): 1]
are not equal
========================
dict1: [AnyHashable("1"): 1]
dict2: [AnyHashable(1): 1]
are not equal
========================
dict1: [AnyHashable(1): 2]
dict2: [AnyHashable(1): 3]
are not equal
========================
dict1: [AnyHashable("key1"): [1, 2, 3, 4]]
dict2: [AnyHashable("key1"): [1, 2, 3, 4]]
are equal
========================
dict1: [AnyHashable("key1"): [1, 2, 3, 4]]
dict2: [AnyHashable("key1"): [2, 1, 3, 4]]
are equal
========================
dict1: [AnyHashable("key1"): [1, 2, 3, 4]]
dict2: [AnyHashable("key1"): [2, 1, 3]]
are not equal
========================
dict1: [AnyHashable("key1"): [1, 2, 3, 4]]
dict2: [AnyHashable("key1"): [1, 2, 3, "4"]]
are not equal
========================
dict1: [AnyHashable("key1"): ["key2": 2]]
dict2: [AnyHashable("key1"): ["key2": 2]]
are equal
========================
dict1: [AnyHashable("key1"): ["key2": 2]]
dict2: [AnyHashable("key1"): ["key2": 3]]
are not equal
========================
dict1: [AnyHashable("key1"): ["key2": 2]]
dict2: [AnyHashable("key1"): ["key2": 3]]
are not equal
Related Topics
Integration New Facebook Sdk by Swift
Maskstobounds VS. Clipstobounds
Format Uitextfield Text Without Having Cursor Move to the End
Navigation Bar Under Status Bar After Video Playback in Landscape Mode
Writing an iOS 8 Share Extension Without a Storyboard
Scrolling Slow on Mobile/iOS When Using Overflow:Scroll
iPhone iOS Will Not Display Box-Shadow Properly
Reverse an Audio File Swift/Objective-C
How to Consecutively Present Two Alert Views Using Swiftui
How to Create Otp Verification Screen and Detect Delete Backward on Multiple Uitextfield Is Swift
Getter VS Computed Property. What Would Warrant Using One of These Approaches Over the Other
How to Make the View Update Instant in Swiftui
Dateformatter Returns Unexpected Date for Timezone
Upload Files to Dropbox from iOS App with Swift
Recursivedescription Method in Swift
Why Safari Shows "No Inspectable Applications" During Remote Debugging with iOS 6 Device