如何提取字典之间的差异?

10

我可以通过以下方式比较两个字典是否匹配:

func ==(lhs: [String: AnyObject], rhs: [String: AnyObject] ) -> Bool {
    return NSDictionary(dictionary: lhs).isEqualToDictionary(rhs)
}

是否有一种简洁的方法可以提取两个字典之间的差异,就像反向交集一样?


1
什么具体的差别?给定两个字典,每一个可能都有另一个中没有的键。对于存在于两者中的键,它们可能有不同的值。这就给你了两个可能的键集合加上一个可能的不同值的集合。 - Tom Harrington
是的,如果一个键在另一个中不存在,那么它将被视为差异。 - TruMan1
2个回答

10

我们可以定义一个运算符来检查两个字典是否包含相同的键以及对于每个键是否具有相同的值。

值必须是可比较的

首先,我们需要使用泛型来要求字典的Value符合Equatable,否则我们将无法比较这些值。

代码

以下是代码:

func ==<Value:Equatable>(left: [String: Value], right: [String: Value]) -> Bool {
    guard left.keys.count == right.keys.count else { return  false }
    let differenceFound = zip(left.keys.sort(), right.keys.sort()).contains { elm -> Bool in
        let leftKey = elm.0
        let rightKey = elm.1
        return leftKey != rightKey || left[leftKey] != right[rightKey]
    }
    return !differenceFound
}

第一行验证字典的条目数是否相同,如果不同则返回 false

下一个代码块对两个字典的键进行排序,并比较每一对键,寻找其中键或值不同的情况。

如果没有发现这样的差异,则说明两个字典具有相同的键和值,因此它们相等。

示例

["a":1, "b": 2] == ["a":1, "b": 2] // true

当然,由于字典内的值没有顺序,因此以下内容仍然是正确的。

["a":1, "b": 2] == ["b":2, "a": 1] // true

在下一个示例中,我们将比较具有不同数量值的两个字典

["a":1, "b": 2] == ["a":1, "b": 2, "c": 3] // false

不等于运算符

既然我们定义了==运算符,Swift还提供了!=运算符(它只是返回==的相反结果),因此我们也可以写成

["a":1, "b": 2] != ["d":4] // true

简短版

同一个操作符也可以用这种更短的方式来书写

func ==<Value:Equatable>(left: [String: Value], right: [String: Value]) -> Bool {
    return
        left.keys.count == right.keys.count &&
        !zip(left.keys.sort(), right.keys.sort()).contains { $0 != $1 || left[$0] != right[$1] }
}

更新

现在给定两个字典 ab,你想执行 a.minus(b) 并获得一个新的字典,其中包含所有在 a 中可用且不在 b 中可用的 (key, value) 对。

这是代码:

extension Dictionary where Key: Comparable, Value: Equatable {
    func minus(dict: [Key:Value]) -> [Key:Value] {
        let entriesInSelfAndNotInDict = filter { dict[$0.0] != self[$0.0] }
        return entriesInSelfAndNotInDict.reduce([Key:Value]()) { (res, entry) -> [Key:Value] in
            var res = res
            res[entry.0] = entry.1
            return res
        }
    }
}

例子

["a":1].minus(["a":2]) // ["a": 1]
["a":1].minus(["a":1]) // [:]
["a":1].minus(["a":1, "b":2]) // [:]
["a":1, "b": 2].minus(["a":1]) // ["b": 2]

@TruMan1:一个问题。给定这两个字典 let dict0 = ["a":1]let dict1= ["b":2]。difference 方法应该返回什么? - Luca Angeletti
好问题:["a": 1, "b": 2] - TruMan1
另一个例子:["a":1, "b": 2, "c": 3].difference(["b": 2, "c": 4]) => ["a": 1, "c": 3] - TruMan1
@TruMan1,在你最后的例子中,选择“c:3”而不是“c:4”的逻辑是什么?这两个字典在那里有所不同。 - Tom Harrington
1
@TomHarrington:我认为他想要第一个字典中的所有(key,value)对,而不是第二个字典中的。 - Luca Angeletti
显示剩余5条评论

2
Swift支持像symmetricDifference(:)和subtracting(:)这样的操作,但是针对的是集合而不是字典。根据您的需求,您可以对键进行差异处理,然后使用map重建结果字典。

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