如何检查两个 [String: Any] 是否相同?
How to check if two [String: Any] are identical?
有什么方法可以检查两个 [String: Any] 是否相同?
let actual: [[String: Any]] = [
["id": 12345, "name": "Rahul Katariya"],
["id": 12346, "name": "Aar Kay"]
]
var expected: [[String: Any]]!
if actual == expected {
print("Equal")
}
基本上我希望 Dictionary 符合 Swift 3.
中的 Equatable 协议
对于Xcode7.3,swift2.2
字典的类型是:[String:AnyObject]
或简单地说 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
对于Xcode8.beta6,Swift3
字典定义为:
struct Dictionary<Key : Hashable, Value> : Collection, ExpressibleByDictionaryLiteral
NSDictionary 具有以下便捷初始化程序:
convenience init(dictionary otherDictionary: [AnyHashable : Any])
因此您可以对 Key 使用 AnyHashable 类型,对 Value 使用 Any 类型
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
类型 Any
不是 Swift 中的 Equatable
,因此包括 Any
在内的任何集合类型都不能是 Equatable
。
你可以在 Swift 3/Xcode 8 beta 6:
中写这样的东西
if actual as NSArray == expected as NSArray {
print("Equal")
}
但是,由于 importing id
as Any
刚刚在 beta 6 中引入,因此这种行为可能会在不久的将来改变。
符合 Equatable
放在一边;在这个练习中,您 可以 编写自己的 isEqual
函数来比较两个 [T: Any]
词典中您知道值的 (Equatable
) 类型的子集被 Any
包裹限制为。通过尝试转换为这些类型(例如,在 switch
语句中,如下所示),您可以 在 它们转换为这些给定的类型。例如
// 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
}
}
用法:
/* 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]' */
请注意 as
转换可能会产生误报 (true
) 的危险,因为它允许从两种不同类型映射到其他常见类型,例如将两个派生的 class 实例转换为它们共同的父类型时,切掉派生的 class 差异:
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"
如果您希望将失败的 "known types conversion" 转换为 return nil
(而成功的转换将始终产生 true
/false
,基于随后的平等测试),你可以将上面的内容扩展到(甚至更混乱)
// 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 */
但是请注意,在 false
命中的情况下,上述按值相等性测试的值是短路的,这意味着取决于无序字典(无序集合)的随机顺序,给定两个不相等的字典,特殊情况可能 return nil
以及 false
。这种特殊情况发生在两个不相等值的字典(已知类型值-值对的不相等)中,它们还包含未包含在尝试的转换中的值类型:如果首先命中已知类型的不相等, false
将被 returned,而如果首先命中失败的转换,nil
将被 returned。无论哪种方式,nil
return 意味着调用应该被视为无效,因为调用者声明 allPossibleValueTypesAreKnown
是 true
(转换失败意味着是 false
) .
使用 Swift 5.5,您可以轻松地将其转换为 NSDictionary
,因为它总是成功:
XCTAssertEqual(actual as NSDictionary, expected as NSDictionary)
有什么方法可以检查两个 [String: Any] 是否相同?
let actual: [[String: Any]] = [
["id": 12345, "name": "Rahul Katariya"],
["id": 12346, "name": "Aar Kay"]
]
var expected: [[String: Any]]!
if actual == expected {
print("Equal")
}
基本上我希望 Dictionary 符合 Swift 3.
中的 Equatable 协议对于Xcode7.3,swift2.2
字典的类型是:[String:AnyObject]
或简单地说 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
对于Xcode8.beta6,Swift3
字典定义为:
struct Dictionary<Key : Hashable, Value> : Collection, ExpressibleByDictionaryLiteral
NSDictionary 具有以下便捷初始化程序:
convenience init(dictionary otherDictionary: [AnyHashable : Any])
因此您可以对 Key 使用 AnyHashable 类型,对 Value 使用 Any 类型
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
类型 Any
不是 Swift 中的 Equatable
,因此包括 Any
在内的任何集合类型都不能是 Equatable
。
你可以在 Swift 3/Xcode 8 beta 6:
中写这样的东西if actual as NSArray == expected as NSArray {
print("Equal")
}
但是,由于 importing id
as Any
刚刚在 beta 6 中引入,因此这种行为可能会在不久的将来改变。
符合 Equatable
放在一边;在这个练习中,您 可以 编写自己的 isEqual
函数来比较两个 [T: Any]
词典中您知道值的 (Equatable
) 类型的子集被 Any
包裹限制为。通过尝试转换为这些类型(例如,在 switch
语句中,如下所示),您可以 在 它们转换为这些给定的类型。例如
// 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
}
}
用法:
/* 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]' */
请注意 as
转换可能会产生误报 (true
) 的危险,因为它允许从两种不同类型映射到其他常见类型,例如将两个派生的 class 实例转换为它们共同的父类型时,切掉派生的 class 差异:
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"
如果您希望将失败的 "known types conversion" 转换为 return nil
(而成功的转换将始终产生 true
/false
,基于随后的平等测试),你可以将上面的内容扩展到(甚至更混乱)
// 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 */
但是请注意,在 false
命中的情况下,上述按值相等性测试的值是短路的,这意味着取决于无序字典(无序集合)的随机顺序,给定两个不相等的字典,特殊情况可能 return nil
以及 false
。这种特殊情况发生在两个不相等值的字典(已知类型值-值对的不相等)中,它们还包含未包含在尝试的转换中的值类型:如果首先命中已知类型的不相等, false
将被 returned,而如果首先命中失败的转换,nil
将被 returned。无论哪种方式,nil
return 意味着调用应该被视为无效,因为调用者声明 allPossibleValueTypesAreKnown
是 true
(转换失败意味着是 false
) .
使用 Swift 5.5,您可以轻松地将其转换为 NSDictionary
,因为它总是成功:
XCTAssertEqual(actual as NSDictionary, expected as NSDictionary)