Skip to content

Instantly share code, notes, and snippets.

@fewlinesofcode
Created April 2, 2020 17:56
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fewlinesofcode/3ee969746c698752f0e55603cead94c1 to your computer and use it in GitHub Desktop.
Save fewlinesofcode/3ee969746c698752f0e55603cead94c1 to your computer and use it in GitHub Desktop.
Example of structure comparison in Swift
import Foundation
protocol KeyPathListable {
var allKeyPaths: [String: PartialKeyPath<Self>] { get }
var keyPathsList: [PartialKeyPath<Self>] { get }
}
extension KeyPathListable {
private subscript(checkedMirrorDescendant key: String) -> Any {
Mirror(reflecting: self).descendant(key)!
}
var keyPathsList: [PartialKeyPath<Self>] {
var list = [PartialKeyPath<Self>]()
let mirror = Mirror(reflecting: self)
for case (let key?, _) in mirror.children {
list.append(\Self.[checkedMirrorDescendant: key] as PartialKeyPath)
}
return list
}
var allKeyPaths: [String: PartialKeyPath<Self>] {
var membersTokeyPaths = [String: PartialKeyPath<Self>]()
let mirror = Mirror(reflecting: self)
for case (let key?, _) in mirror.children {
membersTokeyPaths[key] = \Self.[checkedMirrorDescendant: key] as PartialKeyPath
}
return membersTokeyPaths
}
}
struct DataSubstruct: Hashable, KeyPathListable {
let foo: String
let num: Double
}
struct DataStruct: Hashable, KeyPathListable {
let num: Int
let str: String
let arrOfStrings: [String]
let substruct: DataSubstruct
let arrOfSubstructs: [DataSubstruct]
}
extension Hashable where Self: KeyPathListable {
// WARNING: Non-transitive!
func diff(against updated: Self) -> [PartialKeyPath<Self>: AnyHashable] {
guard self.keyPathsList.elementsEqual(updated.keyPathsList) else {
fatalError("Not implemented!")
}
var diff = [PartialKeyPath<Self>: AnyHashable]()
dump(Self.self)
for (_, kp) in self.allKeyPaths {
if
let currentValue = self[keyPath: kp] as? AnyHashable,
let updatedValue = updated[keyPath: kp] as? AnyHashable
{
if currentValue != updatedValue {
diff[kp] = updatedValue
}
}
}
return diff
}
static func == (lhs: Self, rhs: Self) -> Bool {
guard lhs.keyPathsList.elementsEqual(rhs.keyPathsList) else {
fatalError("Not implemented!")
}
for kp in lhs.keyPathsList {
guard let lhsValue = lhs[keyPath: kp] as? AnyHashable,
let rhsValue = rhs[keyPath: kp] as? AnyHashable,
lhsValue == rhsValue
else {
return false
}
}
return true
}
func hash(into hasher: inout Hasher) {
for kp in keyPathsList {
guard let value = self[keyPath: kp] as? AnyHashable else {
continue
}
value.hash(into: &hasher)
}
}
}
let a = DataStruct(
num: 1,
str: "One",
arrOfStrings: ["foo", "bar", "baz"],
substruct: DataSubstruct(foo: "Foo", num: 1),
arrOfSubstructs: [
DataSubstruct(foo: "Bar", num: 1.0),
DataSubstruct(foo: "Baz", num: 2.0)
]
)
let b = DataStruct(
num: 2,
str: "Two",
arrOfStrings: ["foo", "bar", "baz"],
substruct: DataSubstruct(foo: "Foo", num: 1),
arrOfSubstructs: [
DataSubstruct(foo: "Bar", num: 1.0),
DataSubstruct(foo: "Quix", num: 2.0)
]
)
let difference = a.diff(against: b)
difference.keys.forEach {
print(\DataStruct.num == $0)
print(a[keyPath: $0], "->", b[keyPath: $0])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment