How to check if two strings [String: Any] are identical?

Is there a way to check if two [String: Any] are identical?

let actual: [[String: Any]] = [ ["id": 12345, "name": "Rahul Katariya"], ["id": 12346, "name": "Aar Kay"] ] var expected: [[String: Any]]! if actual == expected { print("Equal") } 

Basically, I want the Dictionary to conform to the Equableable protocol in Swift 3.

+6
source share
3 answers

For Xcode 7.3, swift 2.2 Dictionary is of type: [String:AnyObject] or just type 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

The 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 the 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 
+10
source

Any type is not Equatable in Swift, so any collection type, including Any , cannot be Equatable .

You can write something like this in Swift 3 / Xcode 8 beta 6:

 if actual as NSArray == expected as NSArray { print("Equal") } 

But, since importing id as Any just been introduced in beta 6, so this behavior may change in the near future.

+2
source

Matching Equatable aside; for the exercise, you can write your own isEqual function to compare two [T: Any] dictionaries for a subset of Equatable types, which are known to be limited to the value enclosed in Any . When you try to convert to these types (for example, in the switch , as shown below), you can compare the dictionary values โ€‹โ€‹(for each given key) one after the other after converting them to these specified types. For instance.

 // Usable if the 'Any' values in your dict only wraps // a few different types _that are known to you_. // Return false also in case value cannot be successfully // converted to some known type. This might yield a false negative. extension Dictionary where Value: Any { func isEqual(to otherDict: [Key: Any], allPossibleValueTypesAreKnown: Bool = false) -> Bool { guard allPossibleValueTypesAreKnown && self.count == otherDict.count else { return false } for (k1,v1) in self { guard let v2 = otherDict[k1] else { return false } switch (v1, v2) { case (let v1 as Double, let v2 as Double) : if !(v1.isEqual(to: v2)) { return false } case (let v1 as Int, let v2 as Int) : if !(v1==v2) { return false } case (let v1 as String, let v2 as String): if !(v1==v2) { return false } // ... fill in with types that are known to you to be // wrapped by the 'Any' in the dictionaries default: return false } } return true } } 

Using:

 /* example setup */ var dict1: [String: Any] = ["id": 12345, "name": "Rahul Katariya", "weight": 70.7] var dict2: [String: Any] = ["id": 12346, "name": "Aar Kay", "weight": 83.1] /* example usage */ print(dict1.isEqual(to: dict2, allPossibleValueTypesAreKnown: true)) // false dict2["name"] = "Rahul Katariya" dict2["weight"] = 70.7 print(dict1.isEqual(to: dict2, allPossibleValueTypesAreKnown: true)) // false dict2["id"] = 12345 print(dict1.isEqual(to: dict2, allPossibleValueTypesAreKnown: true)) // true class Foo {} dict1["id"] = Foo() dict2["id"] = Foo() print(dict1.isEqual(to: dict2, allPossibleValueTypesAreKnown: true)) // false! (we haven't implemented this attempted conversion!) // incompatable keys cause error as expected an intended let dict3: [Int: Any] = [1:2] dict1.isEqual(to: dict3) /* error: cannot convert value of type '[Int : Any]' to expected argument type '[String : Any]' */ 

Just pay attention to the danger that the as conversion can lead to a false positive value ( true ), since it can allow matching of two different types with a common other type, for example. discarding the differences of derived classes when throwing two instances of a derived class into their common parent type:

 class Base: Equatable {} func ==(lhs: Base, rhs: Base) -> Bool { return true } class DerivedA : Base { let foo = "foo" } class DerivedB : Base { let bar = 4.2 } let a = DerivedA() let b = DerivedB() switch (a, b) { case (let a as Base, let b as Base): print(a == b) default: () } // sliced by conversion! prints "true" 

If you want a failed conversion of known types to return nil (whereas successful conversions will always give true / false , based on subsequent equality testing), you can extend the above (even messier)

 // a 'nil' return here would correspond to an invalid call extension Dictionary where Value: Any { func isEqual(to otherDict: [Key: Any], allPossibleValueTypesAreKnown: Bool = false) -> Bool? { guard allPossibleValueTypesAreKnown else { return nil } guard self.count == otherDict.count else { return false } for (k1,v1) in self { guard let v2 = otherDict[k1] else { return false } switch (v1, v2) { case (let v1 as Double, let v2 as Double) : if !(v1.isEqual(to: v2)) { return false } case (let v1 as Int, let v2 as Int) : if !(v1==v2) { return false } case (let v1 as String, let v2 as String): if !(v1==v2) { return false } // ... case (_ as Double, let v2): if !(v2 is Double) { return false } case (_, _ as Double): return false case (_ as Int, let v2): if !(v2 is Int) { return false } case (_, _ as Int): return false case (_ as String, let v2): if !(v2 is String) { return false } case (_, _ as String): return false default: return nil } } return true } } /* Example as per above will yield (printout): Optional(false) Optional(false) Optional(true) nil */ 

Please note, however, that the above testing for equality by value by value is short-circuited in the case of a false attack, which means that depending on the random order of unordered dictionaries (unordered set), a special case may return nil , as well as false , given two not equal dictionary. This special case occurs in two dictionaries of unequal values โ€‹โ€‹(unevenness for a pair of values โ€‹โ€‹of a significant type), which also contain a value type that is not included in the casting attempt: if the unevenness of the known types first occurs, false will be returned, whereas if the conversion fails first, returns nil . In any case, returning nil means the call should be considered invalid because the caller stated that allPossibleValueTypesAreKnown was true (that a failed conversion means false ).

+1
source

All Articles