首页 > 解决方案 > 字段子集上的 CollectionDifference

问题描述

考虑以下代码片段,它创建了 2 个Foo结构集合的差异

struct Foo {

    let v: String
    let other: Int
}

extension Foo: Hashable {

    func hash(into hasher: inout Hasher) {
        hasher.combine(v)
    }
}

extension Foo: CustomStringConvertible {

    var description: String {
        return v
    }
}

var col1 = [
    Foo(v: "a", other: 1),
    Foo(v: "b", other: 1),
    Foo(v: "c", other: 1),
]

var col2 = [
    Foo(v: "a", other: 1),
    Foo(v: "c", other: 1),
    Foo(v: "d", other: 1),
]

let diff = col2.difference(from: col1)

diff.forEach { change in
    switch change {
    case let .remove(offset, _, _):
        print("remove: \(offset)")
    case let .insert(offset, _, _):
        print("insert: \(offset)")
    }
}

print这将按预期产生以下输出:

remove: 1
insert: 2

现在像这样将other字段更改col2为 2

var col2 = [
    Foo(v: "a", other: 2),
    Foo(v: "c", other: 2),
    Foo(v: "d", other: 2),
]

由于我仅从 生成哈希值v,因此我希望得到相同的输出,但我实际得到的是这个。

remove: 2
remove: 1
remove: 0
insert: 0
insert: 1
insert: 2

我在这里想念什么?

标签: swift

解决方案


BidirectionalCollection.difference(from:)根据元素的相等性计算两个集合的“差异”。由于您没有实现==for Foo,因此编译器会合成一个默认实现,并比较所有(存储的)属性。

采用该Hashable协议可以提高性能,但不影响值的相等性:不同的元素可以具有相同的哈希值。

如果您提供我们自己的Equatableas实现

extension Foo: Equatable {
    static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs.v == rhs.v
    }
}

然后

Foo(v: "a", other: 1) == Foo(v: "a", other: 2) // etc.

并且输出是预期的

remove: 1
insert: 2

或者,使用“BidirectionalCollection.difference(from:by:)”并通过自定义等效测试:

let diff = col2.difference(from: col1, by: { $0.v == $1.v })

推荐阅读