How to Compare Two Dictionaries in Swift

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 is Int, and the Value type of the
dictionary is String.

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

Sample Image

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



Leave a reply



Submit