You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift:如何生成结构体/类属性与[String: Any]字典的差异字典?

没问题,我来帮你搞定这个需求!我们可以借助Swift的反射机制(Mirror)来实现这个功能,不管是结构体还是类,核心思路都是一致的——先获取类型的属性信息,再和输入字典逐一比对,筛选出值不同的键值对。

一、结构体的实现方案

我们可以给目标结构体扩展一个方法,利用反射遍历自身的所有属性,和输入字典做对比:

struct MyStruct {
    var a: Bool = false
    var b: String = "random"
    var c: Int = 2
}

extension MyStruct {
    func getDiff(from input: [String: Any]) -> [String: Any] {
        var diffResult: [String: Any] = [:]
        // 通过Mirror获取结构体的属性反射信息
        let mirror = Mirror(reflecting: self)
        
        for property in mirror.children {
            // 确保属性有名称,且输入字典包含该键
            guard let propertyName = property.label, let inputValue = input[propertyName] else {
                continue
            }
            
            // 优先用可哈希类型做精准对比(覆盖大部分基础类型)
            if let selfValue = property.value as? AnyHashable, let inputHashable = inputValue as? AnyHashable {
                if selfValue != inputHashable {
                    diffResult[propertyName] = inputValue
                }
            } else {
                // 针对数组、字典这类不可哈希的类型,用字符串描述做对比(如果需要更严谨可自定义相等逻辑)
                if "\(property.value)" != "\(inputValue)" {
                    diffResult[propertyName] = inputValue
                }
            }
        }
        
        return diffResult
    }
}

// 测试代码
let myStruct = MyStruct()
let input: [String: Any] = ["b": "test", "c": 2]
let result = myStruct.getDiff(from: input)
print(result) // 输出 ["b": "test"],完全符合预期

二、类的实现方案

类的实现和结构体几乎完全一致,因为Swift的Mirror同样支持类类型的反射。只需要把扩展的对象改成类即可:

class MyClass {
    var a: Bool = false
    var b: String = "random"
    var c: Int = 2
    
    init() {}
}

extension MyClass {
    func getDiff(from input: [String: Any]) -> [String: Any] {
        var diffResult: [String: Any] = [:]
        let mirror = Mirror(reflecting: self)
        
        for property in mirror.children {
            guard let propertyName = property.label, let inputValue = input[propertyName] else {
                continue
            }
            
            if let selfValue = property.value as? AnyHashable, let inputHashable = inputValue as? AnyHashable {
                if selfValue != inputHashable {
                    diffResult[propertyName] = inputValue
                }
            } else {
                if "\(property.value)" != "\(inputValue)" {
                    diffResult[propertyName] = inputValue
                }
            }
        }
        
        return diffResult
    }
}

// 测试代码
let myClass = MyClass()
let inputClass: [String: Any] = ["a": true, "c": 3, "d": "extra"]
let resultClass = myClass.getDiff(from: inputClass)
print(resultClass) // 输出 ["a": true, "c": 3],自动忽略了结构体/类中不存在的键"d"

额外注意事项

  1. 性能考量:反射操作会有一定性能开销,如果这个方法需要被频繁调用,建议手动维护一个属性名称列表(比如定义静态数组static let properties = ["a", "b", "c"]),然后遍历这个列表做对比,避免反射的性能损耗。
  2. 类型安全:因为输入字典是[String: Any],如果输入值的类型和结构体/类属性类型不匹配(比如把Int传给Bool属性),上面的代码会进入字符串对比逻辑。如果需要严格的类型检查,可以在对比前先判断类型是否一致。
  3. 嵌套类型支持:如果结构体/类包含嵌套的自定义类型,上面的代码只会对比顶层属性。如果需要支持嵌套属性的差异对比,需要修改代码增加递归遍历嵌套类型的逻辑。

内容的提问来源于stack exchange,提问作者Kinergy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:21:10