82

Is there an easy way to compare two [String: AnyObject] dictionaries in swift, since it doesn't accept the == operator?

By comparing two dictionaries, I mean checking that they have the same exact keys and for every key they have the same values.

garyh
  • 2,782
  • 1
  • 26
  • 28
bubakazouba
  • 1,633
  • 2
  • 22
  • 34

4 Answers4

140

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
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
22

Swift 4 Update:

Comparing Dictionaries is now native! (Docs here)


Swift 3:

Leo Dabus already has an excellently written post with the accepted solution. However, for me, I found that it needed one more step to be fully usable. As you can see from his code, you need to set your dictionary type to [AnyHashable: Any], or otherwise you'll get Binary operator '==' cannot be applied to two '[String : Any]' operands, to use a dictionary common in deserializing JSON for my example.

Generics to the rescue!:

// Swift 3.0
func == <K, V>(left: [K:V], right: [K:V]) -> Bool {
    return NSDictionary(dictionary: left).isEqual(to: right)
}

or in another case I had, with [String: Any?]:

func == <K, V>(left: [K:V?], right: [K:V?]) -> Bool {
    guard let left = left as? [K: V], let right = right as? [K: V] else { return false }
    return NSDictionary(dictionary: left).isEqual(to: right)
}
Community
  • 1
  • 1
AmitaiB
  • 1,656
  • 1
  • 19
  • 19
  • Where do I define this method? – Avinash Sharma Apr 11 '19 at 10:33
  • This can be done anywhere, so I usually put it in a separate file, usually something like `Foundation+utils.swift` or the like. KIM, Swift 3 is not supported as of Xcode 10.2, so you most likely will not need this anymore. :bittersweet emotions: ;) – AmitaiB Apr 11 '19 at 16:39
11

In Swift 2, when both Key and Value are Equatable, you can use == on the dictionary itself:

public func ==<Key : Equatable, Value : Equatable>(lhs: [Key : Value], rhs: [Key : Value]) -> Bool

And, NSObject is Equatable:

public func ==(lhs: NSObject, rhs: NSObject) -> Bool

In your case, if you are working with Obj-C objects that you want to compare using isEqual:, you can simply use NSObject as your value type (rather than AnyObject).

jtbandes
  • 115,675
  • 35
  • 233
  • 266
0

Without custom type in value of Dictionary, in Swift 2+ you can use the == operator to compare two Dictionary to check if they are equal or not.

But in some cases with custom types as the Dictionary's value (like struct), you must adopt Equatable in order for that custom type to use == operator.

Ex:

// custom type
struct Custom: Equatable {
    var value: Int
}

// MARK: adopting Equatable
func ==(lhs: Custom, rhs: Custom) -> Bool {
    if lhs.value == rhs.value {
        return true
    } else {
        return false
    }
}

Now you can use the == operator to compare two dictionaries:

let dic3: [String: Custom] = ["key1": Custom(value:1), "key2": Custom(value:2)]
let dic4: [String: Custom] = ["key1": Custom(value:1), "key2": Custom(value:2)]

if (dic3 == dic4) {
    print("equal")
} else {
    print("not equal")
}
AmitaiB
  • 1,656
  • 1
  • 19
  • 19
larva
  • 4,687
  • 1
  • 24
  • 44
  • why don't you just write `func == (lhs: [Key, Value], rhs: [Key, Value])`? – rolling_codes Aug 16 '16 at 16:58
  • @NoodleofDeath value of dictionary is not Equatable type. And I want to write function in Swift, that not depend on Cocoa Lib type (like NSObject, NSDictionary) – larva Aug 17 '16 at 00:05