How do I compare two dictionaries in Swift?
As already mentioned by Hot Licks you can use NSDictionary method isEqualToDictionary() to check if they are equal as follow:
let dic1: [String: AnyObject] = ["key1": 100, "key2": 200]
let dic2: [String: AnyObject] = ["key1": 100, "key2": 200]
let dic3: [String: AnyObject] = ["key1": 100, "key2": 250]
println( NSDictionary(dictionary: dic1).isEqualToDictionary(dic2) ) // true
println( NSDictionary(dictionary: dic1).isEqualToDictionary(dic3) ) // false
you can also implement a custom operator "==" as follow:
public func ==(lhs: [String: AnyObject], rhs: [String: AnyObject] ) -> Bool {
return NSDictionary(dictionary: lhs).isEqualToDictionary(rhs)
}
println(dic1 == dic2) // true
println(dic1 == dic3) // false
Xcode 9 • Swift 4
From the docs, dictionary is now defined as a struct:
struct Dictionary<Key : Hashable, Value> : Collection, ExpressibleByDictionaryLiteral
Description
A collection whose elements are key-value pairs. A
dictionary is a type of hash table, providing fast access to the
entries it contains. Each entry in the table is identified using its
key, which is a hashable type such as a string or number. You use that
key to retrieve the corresponding value, which can be any object. In
other languages, similar data types are known as hashes or associated
arrays. Create a new dictionary by using a dictionary literal. A
dictionary literal is a comma-separated list of key-value pairs, in
which a colon separates each key from its associated value, surrounded
by square brackets. You can assign a dictionary literal to a variable
or constant or pass it to a function that expects a dictionary.
Here’s how you would create a dictionary of HTTP response codes and their related messages:
var responseMessages = [200: "OK",
403: "Access forbidden",
404: "File not found",
500: "Internal server error"]
The responseMessages variable is inferred to have type
[Int: String]
.
The Key type of the dictionary isInt
, and the Value type of the
dictionary isString
.
To create a dictionary with no key-value pairs, use an empty dictionary literal ([:]).
var emptyDict: [String: String] = [:]
Any type that conforms to the Hashable protocol can be used as a dictionary’s Key type, including all of Swift’s basic types. You can use your own custom types as dictionary keys by making them conform to the Hashable protocol.
We don't need to define a custom operator anymore:
From the docs:
static func ==(lhs: [Key : Value], rhs: [Key : Value]) -> Bool
Testing:
let dic1 = ["key1": 100, "key2": 200]
let dic2 = ["key1": 100, "key2": 200]
let dic3 = ["key1": 100, "key2": 250]
print(dic1 == dic2) // true
print(dic1 == dic3) // false
In the example above all dictionary keys and values are the same type.
If we try to compare two dictionaries of type [String: Any]
Xcode will complain that Binary operator == cannot be applied to two [String: Any]
operands.
let dic4: [String: Any] = ["key1": 100, "key2": "200"]
let dic5: [String: Any] = ["key1": 100, "key2": "200"]
let dic6: [String: Any] = ["key1": 100, "key2": Date()]
print(dic4 == dic5) // Binary operator == cannot be applied to two `[String: Any]` operands
But we can extend the ==
operator functionality implementing an infix operator, casting Swift Dictionary to NSDictionary and constraining the dictionary values to Hashable Protocol:
Xcode 11 • Swift 5.1
public func ==<K, L: Hashable, R: Hashable>(lhs: [K: L], rhs: [K: R] ) -> Bool {
(lhs as NSDictionary).isEqual(to: rhs)
}
Testing:
let dic4: [String: AnyHashable] = ["key1": 100, "key2": "200"]
let dic5: [String: AnyHashable] = ["key1": 100, "key2": "200"]
let dic6: [String: AnyHashable] = ["key1": 100, "key2": Date()]
print(dic4 == dic5) // true
print(dic4 == dic6) // false
let dic7: [String: String] = [ "key2": "200"]
let dic8: [String: Date] = [ "key2": Date()]
print(dic7 == dic8) // false
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
How do i compare two dictionaries in swift and then append any similarities to a new dictionary?
Your code does not compile
As @Bogdan Farca noted in the comments your second dictionary won't compile because of duplicate key
let dict2 = [2: "carrot", 3: "steak", 1: "apple", 3: "pork"]
A better way to represent the information of dict2
is using the food name as key
and the category ID as value
let dict2 = ["carrot" : 2,"steak": 2,"apple": 1, "pork":3]
I am assuming the food names to be unique.
Better names
In order to make the code more readable we should also use better names so
let categories = [1: "fruit", 2: "vegetable", 3: "meat"]
let foods = ["carrot" : 2, "steak": 3, "apple": 1, "pork":3]
Using a Model
We can finally focus on the solution.
You want as output something like this [Int : Dictionary<String, Array<String>>]
It's a complex combination of dictionaries/arrays, why don't you simply use a model value?
struct FoodCategory {
let categoryID: Int
let categoryName: String
let foods: [String]
}
Now you can just write
let foodCategories = categories.map { cat -> FoodCategory in
let foodNames = foods.filter { $0.value == cat.key }.map { $0.0 }
return FoodCategory(categoryID: cat.key, categoryName: cat.value, foods: foodNames )
}
And this is the result
[
FoodCategory(categoryID: 2, categoryName: "vegetable", foods: ["carrot"]),
FoodCategory(categoryID: 3, categoryName: "meat", foods: ["pork", "steak"]),
FoodCategory(categoryID: 1, categoryName: "fruit", foods: ["apple"])
]
How to extract differences between dictionaries?
We can define an operator to check whether 2 dictionaries contains the same keys and for each key the same value.
Value must be Equatable
First of all we need to use generics to require that the Value
of the dictionaries conforms to Equatable
otherwise we won't be able to compare the values.
The code
Here's the code
func ==<Value:Equatable>(left: [String: Value], right: [String: Value]) -> Bool {
guard left.keys.count == right.keys.count else { return false }
let differenceFound = zip(left.keys.sort(), right.keys.sort()).contains { elm -> Bool in
let leftKey = elm.0
let rightKey = elm.1
return leftKey != rightKey || left[leftKey] != right[rightKey]
}
return !differenceFound
}
The first line verify that the dictionaries have the same number of entries, otherwise false
is returned.
The next block of code sort the keys of both dictionaries and compare each pair looking for a pair where the keys or the values are different.
If such difference is not found then the dictionaries have the same keys and values, so they are equals.
Examples
["a":1, "b": 2] == ["a":1, "b": 2] // true
Of course since values inside a dictionaries don't have an order the following is still true
["a":1, "b": 2] == ["b":2, "a": 1] // true
In the next example we compare 2 dictionaries with a different number of values
["a":1, "b": 2] == ["a":1, "b": 2, "c": 3] // false
The != operator
Since we defined the ==
operator, Swift gifts us the !=
operator (which simply returns the opposite of ==
), so we can also write
["a":1, "b": 2] != ["d":4] // true
Shorter version
The same operator can also be written in this shorter way
func ==<Value:Equatable>(left: [String: Value], right: [String: Value]) -> Bool {
return
left.keys.count == right.keys.count &&
!zip(left.keys.sort(), right.keys.sort()).contains { $0 != $1 || left[$0] != right[$1] }
}
Update
Now given 2 dictionaries a
and b
, you want to perform a.minus(b)
and get as result a new dictionaries containing all the (key, value)
pairs available in a
and not available in b
.
Here's the code
extension Dictionary where Key: Comparable, Value: Equatable {
func minus(dict: [Key:Value]) -> [Key:Value] {
let entriesInSelfAndNotInDict = filter { dict[$0.0] != self[$0.0] }
return entriesInSelfAndNotInDict.reduce([Key:Value]()) { (res, entry) -> [Key:Value] in
var res = res
res[entry.0] = entry.1
return res
}
}
}
Examples
["a":1].minus(["a":2]) // ["a": 1]
["a":1].minus(["a":1]) // [:]
["a":1].minus(["a":1, "b":2]) // [:]
["a":1, "b": 2].minus(["a":1]) // ["b": 2]
How to check if two [String: Any] are identical?
For Xcode 7.3, swift 2.2
A dictionary is of type : [String:AnyObject]
or simply put NSDictionary
let actual: [String: AnyObject] = ["id": 12345, "name": "Rahul Katariya"]
var expected: [String: AnyObject] = ["id": 12346, "name": "Aar Kay"]
print(NSDictionary(dictionary: actual).isEqualToDictionary(expected))//False
For Xcode 8.beta 6, Swift 3
Dictionary is defined as:
struct Dictionary<Key : Hashable, Value> : Collection, ExpressibleByDictionaryLiteral
NSDictionary has the following convenience initializer:
convenience init(dictionary otherDictionary: [AnyHashable : Any])
So you can use AnyHashable type for Key and Any type for Value
let actual: [String: Any] = ["id": 12345, "name": "Rahul Katariya"]
var expected: [String: Any] = ["id": 12346, "name": "Aar Kay"]
print(NSDictionary(dictionary: actual).isEqual(to: expected))//False
how to compare two dictionaries which has two different count values to check pk and update the main dictionary and retrieve when table reloads
Please Check below codes :
var productArray: [[String: Any]] = [
[
"pk": 1,
"quantity": 0
],
[
"pk": 2,
"quantity": 0
],
[
"pk": 3,
"quantity": 0
],
[
"pk": 4,
"quantity": 0
]
]
let chartArray: [[String: Any]] = [
[
"pk": 1,
"quantity": 100
],
[
"pk": 2,
"quantity": 100
],
[
"pk": 3,
"quantity": 100
]
]
productArray = productArray.compactMap({ (product: [String: Any]) -> [String : Any]? in
for chart in chartArray {
guard let chartQuantity = chart["pk"] as? Int,
let chartPK = chart["pk"] as? Int,
let productPK = product["pk"] as? Int,
chartPK == productPK else {
continue
} // pass if have same pk value
return [
"pk": chartPK,
"quantity": chartQuantity
]
}
return product
})
print(productArray.description)
Related Topics
What Does It Mean That String and Character Comparisons in Swift Are Not Locale-Sensitive
How to Compare One Value Against Multiple Values - Swift
Forced to Cast, Even If Protocol Requires Given Type
Swift Random Float Between 0 and 1
Instance Member Cannot Be Used on Type of Custom Class
Can Swift Return Value from an Async Void-Returning Block
Using Dateformatter on a Unix Timestamp
Nsfilemanager Fileexistsatpath:Isdirectory and Swift
Multi-Touch Gesture in Sprite Kit
How to Move Enemy Towards a Moving Player
Differences in Nsdatecomponents Syntax
Understanding Spritekit Collisionbitmask
What's the Simplest Way to Convert from a Single Character String to an Ascii Value in Swift
How to Encode a String to Base64 in Swift
Using 'Self' in Class Extension Functions in Swift
Swift Compiler Segmentation Fault When Building
How Flatmap API Contract Transforms Optional Input to Non Optional Result