Combining two `CollectionDifference`

Is there a way to combine multiple CollectionDifference into one big difference? I find myself wanting to combine multiple “history” of changes a few times.

As is, I’m using an array of CollectionDifference and apply them sequentially.

I don't see any methods to do it. You could implement it by summing the "change" elements, but this approach is nasty and O(self.count + other.count).

extension CollectionDifference {
    func merged(with other: CollectionDifference) -> CollectionDifference {
        var changes = Array<Element>()
        changes.reserveCapacity(self.count + other.count)
        changes.append(contentsOf: self)
        changes.append(contentsOf: other)
        return CollectionDifference(changes)!
    }
}

let abcd = ["a", "b", "c", "d"]
let abd = ["a", "b", "d"]
let abc = ["a", "b", "c"]

let diffRemoveC = abd.difference(from: abcd)
let diffRemoveD = abc.difference(from: abcd)

print(diffRemoveC)
print(diffRemoveD)

let diffRemoveCandD = diffRemoveC.merged(with: diffRemoveD)

print(diffRemoveCandD)

Alternatively, I think you can avoid the intermediate allocation with a flatMap, using lazy:

return CollectionDifference([self, other].lazy.flatMap { $0 })!

What I want actually is not to combine 2 different changes together, but to combine 2 consecutive changes.

It'd be more like:

let v1 = ...
let v2 = ...
let v3 = ...

let diff1 = v2.difference(from: v1)
let diff2 = v3.difference(from: v2)

// How to get this from diff1 & diff2
let totalDiff = v3.difference(from: v1)

Simply concatenating each Changes won't work. Applying & re-Diffing can also be costly if the collection is large compared to diff.

Combining CollectionDifference like that is also risky if the diff decides to remove the same element (which is possible), though that could be seen as merge conflict.

1 Like

To answer my own question, this (github) seems to be working.