Skip to content

Instantly share code, notes, and snippets.

@simme
Created August 10, 2016 13:43
Show Gist options
  • Save simme/b2baab56800b02b17fc6282a9740be98 to your computer and use it in GitHub Desktop.
Save simme/b2baab56800b02b17fc6282a9740be98 to your computer and use it in GitHub Desktop.
Utility for diffing two arrays of arrays.
enum DiffModification<T: Equatable> {
case inserted(position: NSIndexPath, value: T)
case deleted(position: NSIndexPath, value: T)
case moved(position: NSIndexPath, newPosition: NSIndexPath, value: T)
}
extension DiffModification: CustomDebugStringConvertible {
var debugDescription: String {
switch self {
case .inserted(let position, let value):
return "Inserted \(value) at \(position.section):\(position.item)"
case .deleted(let position, let value):
return "Deleted \(value) at \(position.section):\(position.item)"
case .moved(let position, let newPosition, let value):
return "Moved \(value) from \(position.section):\(position.item) to \(newPosition.section):\(newPosition.item)"
}
}
}
struct Diff<T: Equatable> {
let newCollection: [[T]]
let oldCollection: [[T]]
init(new: [[T]], old: [[T]]) {
self.oldCollection = old
self.newCollection = new
}
lazy var diff: [DiffModification<T>] = {
let newMap = self.diffMap(self.newCollection)
let oldMap = self.diffMap(self.oldCollection)
var oldItemsHandled = [T]()
var log = newMap.map({ (indexPath, item) -> DiffModification<T>? in
guard let oldIndex = oldMap.indexOf({ indexPath, question in question == item }) else {
return .inserted(position: indexPath, value: item)
}
defer { oldItemsHandled.append(item) }
let oldItemPosition = oldMap[oldIndex].0
let oldItemValue = oldMap[oldIndex].1
// Return now if the item has not moved
if oldItemPosition == indexPath { return nil }
return .moved(position: oldItemPosition, newPosition: indexPath, value: item)
}).flatMap { $0 }
log += oldMap.filter({ indexPath, item in
return oldItemsHandled.indexOf(item) == nil
}).map({ indexPath, item in
return DiffModification.deleted(position: indexPath, value: item)
})
return log
}()
private func diffMap(collection: [[T]]) -> [NSIndexPath: T] {
var map = [NSIndexPath: T]()
for section in 0..<collection.count {
for item in 0..<collection[section].count {
let indexPath = NSIndexPath(forItem: item, inSection: section)
map[indexPath] = collection[section][item]
}
}
return map
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment