如何在Swift中递归比较两个可编码对象并获得差异?



我有两个Lecture的实例,即oldLecturenewLectureLecture的简化声明如下(完整版本有更多字段):

struct Lecture: Codable {
let title: String
let class_times: [ClassTime]
let color: [String: String]
}
struct ClassTime: Codable {
let start: Double
let len: Double
}

现在我需要递归地比较oldLecturenewLecture,并创建一个新的类似字典的对象,该对象只包含newLecture中修改的属性。结果对象将作为PUT请求发送到服务器。

例如,

var oldLecture = Lecture(title: "lecture1", 
class_times: [ClassTime(start: 1, len: 2)], 
color: ["fg": "#fff", "bg": "#000"])
var newLecture = Lecture(title: "lecture1", 
class_times: [ClassTime(start: 2, len: 3)], 
color: ["fg": "#fff", "bg": "#000"])
// the result object should only contain `class_times` key with the value of `[ClassTime(start: 2, len: 3)]`

我已经尝试过了

guard let oldDict = oldLecture.asDictionary() else { return nil }
guard let newDict = newLecture.asDictionary() else { return nil }
var dict: [String: Any] = [:]
for (key, newVal) in newDict {
if let oldVal = oldDict[key], oldVal != newVal {
dict[key] = newVal
}
}

上面的方法不起作用,因为它没有递归地比较oldValnewVal

有没有更干净的方法来实现这一点,而不使用第三方库,如SwiftyJSON?

我想,你应该研究一下集合和集合算术。
这段代码可能不完全是您想要的,但它应该为您指明正确的方向。

通过创建两个讲座的时间的对称不同集合,我们得到一个讲座中出现的时间列表,但不是两个讲座。

struct Lecture: Codable, Equatable {
let title: String
let times: [LectureTime]
let color: [String: String]
}
struct LectureTime: Codable, Hashable {
let start: Double
let len: Double
}
var oldLecture = Lecture(title: "lecture1",
times: [.init(start: 1, len: 2), .init(start: 5, len: 2)],
color: ["fg": "#fff", "bg": "#000"])
var newLecture = Lecture(title: "lecture1",
times: [.init(start: 2, len: 3), .init(start: 5, len: 2)],
color: ["fg": "#fff", "bg": "#000"])

if oldLecture != newLecture {
let oldTimes = Set(oldLecture.times)
let newTimes = Set(newLecture.times)
let x = oldTimes.symmetricDifference(newTimes)
print(String(describing:x))
}

打印

[LectureTime(start: 1.0, len: 2.0), LectureTime(start: 2.0, len: 3.0)]

可能你想要扩展你的时间模型,使其可识别——这样你就可以识别出时间被改变了。

获取newLecture和oldLecture之间的所有差异(反之亦然),并将其发送到服务器,尝试这种方法,将Equatable用于ClassTime,这将比较ClassTime对象的内容。getDiff(...)的结果给出了Lecture,title字段的标题不同,colorclass_times字段的标题也不同。

let result = getDiff(old: oldLecture, new: newLecture)
print("n---> result: (result)")
do {
let data = try JSONEncoder().encode(result)
print("---> data: (data)")
// --> send data to your server
if let str = String(data: data, encoding: .utf8) {
print("---> this is the json object you are sending: (str)")
}
} catch {
print("---> error: (error)")
}

where:

func getDiff(old: Lecture, new: Lecture) -> Lecture {
let title = old.title == new.title ? "" : new.title
let color: [String: String] = new.color.filter{ dic in
!old.color.contains(where: { $0.key == dic.key && $0.value == dic.value })
}
let cls = new.class_times.filter{ !old.class_times.contains($0)}
return Lecture(title: title, class_times: cls, color: color)
}

:

struct ClassTime: Codable, Equatable {  // <-- here
let start: Double
let len: Double
}

最新更新