在Swift中如何比较两个字典?

84

在Swift中有没有一种简单的方法来比较两个[String: AnyObject]字典,因为它不接受==运算符?

通过比较两个字典,我的意思是检查它们具有相同的确切键,并且对于每个键,它们具有相同的值。

4个回答

140

如Hot Licks所提到的,您可以使用NSDictionary方法isEqualToDictionary()来检查它们是否相等,示例如下:

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

你也可以按照以下方式实现自定义运算符"==":

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

根据文档,字典现在被定义为一个结构体:

struct Dictionary<Key : Hashable, Value> : Collection, ExpressibleByDictionaryLiteral

描述

一个由键值对组成的集合。字典是一种哈希表类型,提供快速访问其包含的条目。表中的每个条目都使用其键标识,该键是可散列类型,例如字符串或数字。您使用该键来检索相应的值,该值可以是任何对象。在其他语言中,类似的数据类型称为哈希表或关联数组。通过使用字典字面量创建新字典。字典字面量是一个由逗号分隔的键值对列表,其中冒号将每个键与其关联的值分隔开,并用方括号括起来。您可以将字典字面量分配给变量或常量,也可以将其传递给期望字典的函数。

以下是如何创建HTTP响应代码及其相关消息的字典:

var responseMessages = [200: "OK",
                        403: "Access forbidden",
                        404: "File not found",
                        500: "Internal server error"]

变量responseMessages被推断为类型为[Int: String]的字典。 字典的键类型是Int,值类型是String

要创建一个没有键值对的字典,请使用空字典文本([:])。

var emptyDict: [String: String] = [:]
任何符合 Hashable 协议的类型都可以被用作字典的键类型,包括 Swift 的所有基本类型。通过使自定义类型符合 Hashable 协议,您可以将其用作字典键。


我们不再需要定义自定义运算符了:

从文档中得知:

static func ==(lhs: [Key : Value], rhs: [Key : Value]) -> Bool

测试:

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
在上面的例子中,所有字典键和值都是相同的类型。如果我们尝试比较两个类型为[String: Any]的字典,Xcode会抱怨二元运算符==不能应用于两个[String: Any]操作数。
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

但我们可以通过实现一个中缀运算符扩展==操作符的功能,将Swift字典转换为NSDictionary并对字典值进行约束以符合Hashable协议:


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)
}
测试:
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

3
这似乎是一个很好的通用类和协议类型约束的候选者。 - Anjan Biswas

22

Swift 4 更新:

比较字典现在是原生的了!(文档在这里


Swift 3:

Leo Dabus已经写了一篇非常好的帖子,有被接受的解决方案。然而,对我来说,我发现它还需要一个步骤才能完全可用。从他的代码中可以看出,你需要将字典类型设置为[AnyHashable: Any],否则你会得到二进制运算符“==”无法应用于两个“[String:Any]”操作数,以使用在我的例子中反序列化JSON常见的字典。

泛型拯救!:

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

或者另一个例子是与 [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)
}

我应该在哪里定义这个方法? - Avinash Sharma
这可以在任何地方完成,所以我通常会把它放在一个单独的文件中,通常是像 Foundation+utils.swift 这样的东西。KIM,Xcode 10.2 不支持Swift 3,所以你很可能不再需要它了。: 又甜又苦的情绪:;) - AmitaiB

11
在 Swift 2 中,当 Key 和 Value 都是 Equatable 时,你可以在字典本身上使用 ==
public func ==<Key : Equatable, Value : Equatable>(lhs: [Key : Value], rhs: [Key : Value]) -> Bool

并且,NSObject是可比较的:

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

如果你想使用 isEqual: 比较 Obj-C 对象,那么你可以使用 NSObject 作为值类型(而不是 AnyObject)。


很遗憾,我没有使用Swift 2。 - bubakazouba
3
很快你就能用上iOS9了,它即将在几天内发布。 - zaph

0

在Swift 2+中,如果字典的值没有自定义类型,您可以使用==运算符比较两个Dictionary以检查它们是否相等。

但是,在某些情况下,如果字典的值是自定义类型(如struct),则必须采用Equatable才能使用==运算符。

例如:

// 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
    }
}

现在您可以使用 == 运算符来比较两个字典:
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")
}

为什么不直接写成 func == <Key: Equatable, Value: Equatable>(lhs: [Key, Value], rhs: [Key, Value]) 呢? - rolling_codes
@NoodleofDeath 字典的值不是可比较类型。我想用 Swift 编写一个函数,它不依赖于 Cocoa Lib 类型(如 NSObject、NSDictionary)。 - larva

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接