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

Swift中[String: [Interests]]按键排序后保存乱序的解决咨询

问题原因

Swift 中的Dictionary(即[Key: Value]类型)基于哈希表实现,本身不保证键值对的顺序。哪怕你按有序的键依次插入,后续的打印、编码或其他操作都可能打乱顺序——这就是你看到sortedKeys是有序的,但赋值到sortedData后打印又乱掉的核心原因。

解决方案

根据需求,提供三种可行方案:

方案1:使用有序字典(推荐)

Swift 官方维护的Collections库提供了OrderedDictionary类型,它会严格保留键值对的插入顺序,同时具备普通字典的访问效率。

步骤:

  1. 引入Collections库:通过 Swift Package Manager 添加依赖,或用 CocoaPods 导入SwiftCollections。
  2. 修改结构体代码:
import Collections

// MARK: - InterestsData
struct InterestsData: Codable {
    let message: String?
    let data: OrderedDictionary<String, [Interests]>

    func sortedByKey() -> InterestsData {
        let sortedKeys = data.keys.sorted()
        var sortedData = OrderedDictionary<String, [Interests]>()
        for key in sortedKeys {
            sortedData[key] = data[key]
        }
        return InterestsData(message: message, data: sortedData)
    }
}

// MARK: - Interests
struct Interests: Codable {
    let id: Int
    let name: String
    let isSelected: Int?
}

修改后,排序后的键值对顺序会被永久保留,打印、编码操作都不会打乱顺序。

方案2:改用数组存储有序键值对

如果不想引入外部库,可以将data的类型改为存储键值对元组的数组,完全手动控制顺序。

// MARK: - InterestsData
struct InterestsData: Codable {
    let message: String?
    let data: [(key: String, value: [Interests])]

    // 从普通字典初始化,自动排序
    init(message: String?, dataDict: [String: [Interests]]) {
        self.message = message
        self.data = dataDict.sorted(by: { $0.key < $1.key })
    }

    // 可选:转回普通字典的方法
    func toDictionary() -> [String: [Interests]] {
        Dictionary(uniqueKeysWithValues: data)
    }
}

// MARK: - Interests
struct Interests: Codable {
    let id: Int
    let name: String
    let isSelected: Int?
}

这种方式适合需要严格控制展示顺序的场景,缺点是访问特定键的值时需要遍历数组或转换为字典。

方案3:自定义Codable保证序列化后的顺序

如果必须使用普通Dictionary,可以通过自定义编码逻辑,让序列化后的 JSON 保持有序(内存中的字典仍无序,但保存/传输的结果是有序的)。

// MARK: - InterestsData
struct InterestsData: Codable {
    let message: String?
    let data: [String: [Interests]]

    func sortedByKey() -> InterestsData {
        let sortedKeys = data.keys.sorted()
        var sortedData: [String: [Interests]] = [:]
        for key in sortedKeys {
            sortedData[key] = data[key]
        }
        return InterestsData(message: message, data: sortedData)
    }

    enum CodingKeys: String, CodingKey {
        case message, data
    }

    // 自定义编码,按排序后的键写入JSON
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(message, forKey: .message)
        
        let sortedKeys = data.keys.sorted()
        var dataContainer = container.nestedContainer(keyedBy: DynamicCodingKey.self, forKey: .data)
        for key in sortedKeys {
            let codingKey = DynamicCodingKey(stringValue: key)!
            try dataContainer.encode(data[key], forKey: codingKey)
        }
    }

    // 自定义解码,按键排序后存入字典
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        message = try container.decodeIfPresent(String.self, forKey: .message)
        
        let dataContainer = try container.nestedContainer(keyedBy: DynamicCodingKey.self, forKey: .data)
        var dataDict: [String: [Interests]] = [:]
        for key in dataContainer.allKeys.sorted(by: { $0.stringValue < $1.stringValue }) {
            let interests = try dataContainer.decode([Interests].self, forKey: key)
            dataDict[key.stringValue] = interests
        }
        data = dataDict
    }
}

// 动态CodingKey处理自定义字符串键
struct DynamicCodingKey: CodingKey {
    let stringValue: String
    init?(stringValue: String) {
        self.stringValue = stringValue
    }
    let intValue: Int?
    init?(intValue: Int) {
        self.intValue = intValue
        self.stringValue = String(intValue)
    }
}

// MARK: - Interests
struct Interests: Codable {
    let id: Int
    let name: String
    let isSelected: Int?
}

这个方案适合需要保证存储/传输的JSON有序,但内存中仍需使用字典的场景。

内容的提问来源于stack exchange,提问作者Kegham K.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 22:27:45