I have two instances of Lecture
which are oldLecture
and newLecture
. A simplified declaration of Lecture
is as follows (the full version has many more fields):
struct Lecture: Codable {
let title: String
let class_times: [ClassTime]
let color: [String: String]
}
struct ClassTime: Codable {
let start: Double
let len: Double
}
Now I need to recursively compare oldLecture
and newLecture
, and create a new dictionary-like object that contains only the properties that are modified in newLecture
. The resulting object will be sent to the server as PUT
request.
For example,
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)]`
What I have tried so far
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
}
}
The approach above won't work because it doesn't compare oldVal
and newVal
recursively.
Is there a cleaner way to achieve this, without using third-party libraries such as SwiftyJSON
?
CodePudding user response:
to get the all the differences between the newLecture and oldLecture (or vice versa),
and send that to your server, try this approach, using Equatable
for ClassTime
, that will compare the content of ClassTime
objects.
The results of getDiff(...)
gives you a Lecture
, with the title differences in the title
field, and similarly for color
and class_times
fields.
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)
}
with:
struct ClassTime: Codable, Equatable { // <-- here
let start: Double
let len: Double
}
CodePudding user response:
I think, you want to look into Sets and Set Arithmetics.
This code is probably not fully what you want, but it should point you in the right direction.
By creating a symetric different set of the times of both lectures we get a list of times that are in either lecture, but not both.
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))
}
prints
[LectureTime(start: 1.0, len: 2.0), LectureTime(start: 2.0, len: 3.0)]
Probably you want to extend your time model to be identifiable— than you could identify, what time has been changed.