【问题标题】:How do I compare two dictionaries in Swift?如何在 Swift 中比较两个字典?
【发布时间】:2017-12-09 04:00:47
【问题描述】:

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

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

【问题讨论】:

    标签: swift dictionary compare


    【解决方案1】:

    正如 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
    

    说明

    元素是键值对的集合。一种 字典是一种哈希表,提供快速访问 它包含的条目。表中的每个条目都使用其标识 key,它是一种可散列的类型,例如字符串或数字。你用那个 key 来检索对应的值,可以是任何对象。在 其他语言,类似的数据类型被称为散列或关联 数组。使用字典文字创建一个新字典。一种 字典文字是一个逗号分隔的键值对列表,在 冒号将每个键与其关联的值分开,包围 用方括号。您可以将字典文字分配给变量 或常量或将其传递给需要字典的函数。

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

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

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

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

    var emptyDict: [String: String] = [:]
    

    任何符合 Hashable 协议的类型都可以用作字典的 Key 类型,包括 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 Dictionary 转换为 NSDictionary 并将字典值限制为 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)
    }
    

    测试:

    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
    

    【讨论】:

    • 这看起来很适合泛型,一种使用类型约束的协议。
    【解决方案2】:

    Swift 4 更新:

    比较字典现在是原生的! (文档here


    斯威夫特 3:

    Leo Dabus 已经发表了一篇优秀的文章,其中包含公认的解决方案。然而,对我来说,我发现它需要多一步才能完全可用。从他的代码中可以看出,您需要将字典类型设置为[AnyHashable: Any],否则您将获得Binary operator '==' cannot be applied to two '[String : Any]' operands,以在我的示例中使用反序列化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)
    }
    

    【讨论】:

    • 我在哪里定义这个方法?
    • 这可以在任何地方完成,所以我通常把它放在一个单独的文件中,通常是Foundation+utils.swift之类的。 KIM,从 Xcode 10.2 开始不支持 Swift 3,因此您很可能不再需要它。 :苦乐参半的情绪: ;)
    【解决方案3】:

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

    但在某些将自定义类型作为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 == &lt;Key: Equatable, Value: Equatable&gt;(lhs: [Key, Value], rhs: [Key, Value])
    • @NoodleofDeath 字典的值不是 Equatable 类型。我想用 Swift 编写函数,不依赖于 Cocoa Lib 类型(如 NSObject、NSDictionary)
    【解决方案4】:

    在 Swift 2 中,当 KeyValue 都是 Equatable 时,您可以在字典本身上使用 ==

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

    而且,NSObject 是 Equatable:

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

    在您的情况下,如果您正在处理要使用 isEqual: 比较的 Obj-C 对象,您可以简单地使用 NSObject 作为您的值类型(而不是 AnyObject)。

    【讨论】:

    • 很遗憾我没有使用 swift 2
    • 你很快就会真正的,iOS9的发布在即,再过几天。
    猜你喜欢
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 2014-03-03
    • 2011-10-08
    • 2018-06-13
    相关资源
    最近更新 更多