Swift中如何递归比较两个Codable对象并获取差异?
递归比较Swift Codable实例生成差异字典
需求说明
现有两个Lecture结构体实例oldLecture和newLecture,结构体定义如下:
struct Lecture: Codable { let title: String let class_times: [ClassTime] let color: [String: String] } struct ClassTime: Codable { let start: Double let len: Double }
需要递归对比两个实例的所有属性(包括嵌套结构体、数组、字典),生成一个仅包含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"]) // 预期结果:仅包含`class_times`键,对应值为`[["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 } }
解决方案(无第三方库)
我们可以实现一个递归对比函数,同时给Encodable添加扩展,将实例转为字典用于对比:
1. Encodable转字典扩展
先实现一个通用的扩展,把任意Codable实例转为字典:
extension Encodable { func asDictionary() -> [String: Any]? { guard let data = try? JSONEncoder().encode(self) else { return nil } return try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] } }
2. 递归对比差异函数
编写递归函数,处理字典、数组、基础类型的对比:
func generateDiff(oldDict: [String: Any]?, newDict: [String: Any]?) -> [String: Any]? { guard let old = oldDict, let new = newDict else { return new } var diffResult: [String: Any] = [:] for (key, newValue) in new { let oldValue = old[key] switch (oldValue, newValue) { // 处理嵌套字典 case let (oldNested as [String: Any], newNested as [String: Any]): if let nestedDiff = generateDiff(oldDict: oldNested, newDict: newNested), !nestedDiff.isEmpty { diffResult[key] = nestedDiff } // 处理数组:若数组元素不同,直接保留新数组(如果需要更细粒度的数组元素对比,可以扩展此处逻辑) case let (oldArray as [Any], newArray as [Any]): if oldArray != newArray { diffResult[key] = newValue } // 处理基础类型(字符串、数字等) default: if oldValue != newValue { diffResult[key] = newValue } } } return diffResult.isEmpty ? nil : diffResult }
3. 使用示例
let oldLecture = Lecture(title: "lecture1", class_times: [ClassTime(start: 1, len: 2)], color: ["fg": "#fff", "bg": "#000"]) let newLecture = Lecture(title: "lecture1", class_times: [ClassTime(start: 2, len: 3)], color: ["fg": "#fff", "bg": "#000"]) if let oldDict = oldLecture.asDictionary(), let newDict = newLecture.asDictionary() { if let diff = generateDiff(oldDict: oldDict, newDict: newDict) { print(diff) // 输出:["class_times": [["start": 2, "len": 3]]] // 可直接将此字典转为JSON数据发送PUT请求 } }
补充说明
- 如果需要对数组元素做更细粒度的递归对比(比如数组中单个结构体元素修改时,只保留该元素的差异),可以扩展数组分支的逻辑,逐个对比数组元素并生成差异数组。
- 该实现完全基于Swift标准库,无需依赖任何第三方框架。
内容的提问来源于stack exchange,提问作者shinhong
相关产品推荐
相关产品推荐

