【问题标题】:Sort "allKeys" with mapping使用映射对“allKeys”进行排序
【发布时间】:2018-05-06 09:51:32
【问题描述】:

我使用动态字符串键解码 JSON,如下所示:

{
"dynamickey1":
    {"infos1": "blabla","infos2": 21},
"dynamickey2":
    {"infos1": "blabla","infos2": 12},
... }

它们按我想要的方式排序,因为我在 URL 中放了一个字符串列表。但是当它使用下面的代码解码字典时(编辑:我放了完整的代码),我失去了顺序:

struct Devise {
    let nom : String
    let EUR: Float
    let ETH: Float
}

struct DataPrix {
    let devises: [Devise]
    struct DataPrixCodingKeys: CodingKey {
        let stringValue : String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        var intValue : Int? {return nil}
        init?(intValue: Int) {return nil}
    }

    enum DeviseCodingKeys: String, CodingKey {
        case ETH
        case EUR
    }
}

let decoder = JSONDecoder()

extension DataPrix: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy : DataPrixCodingKeys.self)
        self.devises = try (container.allKeys).map { key in
            let devisesContainer = try container.nestedContainer(keyedBy: DeviseCodingKeys.self, forKey: key)
            let deviseName = key.stringValue
            let EUR  = try devisesContainer.decode(Float.self, forKey: .EUR)
            let ETH  = try devisesContainer.decode(Float.self, forKey: .ETH)
            return Devise(nom: deviseName, EUR: EUR, ETH: ETH)
        }
    }
}

那么此时是否可以按字母顺序对它们进行排序,或者至少保持与我在初始列表中的顺序相同?

【问题讨论】:

  • 一如既往,不要在 Swift 中使用NSDictionary。
  • 它是如何“不工作”的?
  • @Sweeper 对不起我编辑了,我有错误Ambiguous reference to member '<'
  • 尝试使用(<) 将运算符称为函数而不是内联的应用程序。
  • @ItaiFerber 同样的错误

标签: json swift swift4


【解决方案1】:

这更有可能是container 类型的问题。看来您正在使用NSDictionary,它的allKeys 类型为Any。 Any 不是 Comparable。

最简单的解决方案是将字典转换为[String: Any]。然后可以直接使用container.keys.sorted(),其中<是默认比较器。

let jsonString = """
{
  "dynamickey1": {"infos1": "blabla","infos2": 21},
  "dynamickey2": {"infos1": "blabla","infos2": 12}
}
"""

let jsonData = jsonString.data(using: .utf8)!
if let container = (try? JSONSerialization.jsonObject(with: jsonData, options: [])) as? [String: Any] {
    let sortedKeys = container.keys.sorted()
    print(sortedKeys)
}

或者,切换到使用Codable 协议并安全地解码 JSON 类型:

let jsonString = """
{
  "dynamickey1": {"infos1": "blabla","infos2": 21},
  "dynamickey2": {"infos1": "blabla","infos2": 12}
}
"""

let jsonData = jsonString.data(using: .utf8)!

struct Info: Codable {
    let infos1: String
    let infos2: Int
}

let parsed = try? JSONDecoder().decode([String: Info].self, from: jsonData)

if let container = parsed {
    print(container.keys.sorted())
}

编辑更新问题的解决方案:

let jsonString = """
{"XPR": {"EUR":0.6866,"ETH":0.001088}}
"""

let jsonData = jsonString.data(using: .utf8)!

enum Currency: String, CodingKey, Comparable {
    case XPR
    case EUR
    case ETH

    public static func < (lhs: Currency, rhs: Currency) -> Bool {
        return lhs.rawValue < rhs.rawValue
    }
}

struct Devise: Decodable {
    let name: String
    let rates: [Currency: Double]

    public init(from decoder: Decoder) throws {
        name = decoder.codingPath.last?.stringValue ?? ""
        let container = try decoder.container(keyedBy: Currency.self)

        var rates: [Currency: Double] = [:]
        try container.allKeys.forEach {
            rates[$0] = try container.decode(Double.self, forKey: $0)
        }
        self.rates = rates
    }
}

struct DataPrix: Decodable {
    let devises: [Devise]

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Currency.self)
        devises = try container.allKeys.sorted().map {
            return try container.decode(Devise.self, forKey: $0)
        }
    }
}

let decoder = JSONDecoder()
do {
  let exchangeRates = try decoder.decode(DataPrix.self, from: jsonData)
  print(exchangeRates)
} catch {
  print(error)
}

但是,如果您的键是真正的动态,您就无法避免使用字典,最好的办法就是使用它:

let decoder = JSONDecoder()
do {
  let exchangeRates = try decoder.decode([String: [String: Double]].self, from: jsonData)
  print(exchangeRates)
} catch {
  print(error)
}

【讨论】:

  • 是否考虑了动态键?我想转换字典时出错,我用完整的代码编辑了我的原始帖子!
  • @TmSmth 我真的很想知道您为什么还要使用带有“动态键”的系统。您能否发布您尝试解析的 JSON?为什么不直接解析[String: String]?
  • 因为如果我理解正确,结构和枚举中变量/大小写的名称必须与 json 字典相同。但是,我请求资产的价格,因此动态键是资产的名称,因此我无法创建只有一个名称的变量/案例。所以 URL 可以是 min-api.cryptocompare.com/data/… 或 min-api.cryptocompare.com/data/…
  • @TmSmth 动态键的全部意义在于在没有字典的情况下模拟字典......最终你会得到一个NSDictionary。为什么不直接解析成[String: [String: Double]]?
  • @TmSmth 好吧,那是您要确保键仅具有某些特定值的时候。例如,如果您对每种受支持的货币或类似的东西都有一个名称映射。
【解决方案2】:

你可以这样排序:

let dictionary  = ["key1":"test","key0":"","key5":""]
dictionary.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedAscending }.map { key in
    // code
} 

所以对于您的代码,您可以像这样使用

  let keys = dic.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedAscending }
    print( keys.map { (key) -> [String : Devise]? in
        guard let value = dic[key] else {return nil}
        return [key : value]
        }.compactMap({$0}))

【讨论】:

  • 我无法将字典转换为[String:Any] 我用完整的代码编辑了我的原始帖子
  • @TmSmth 检查更新的代码,如果你愿意,我有可编码的代码
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-20
  • 1970-01-01
  • 1970-01-01
  • 2018-12-07
  • 2018-01-15
  • 1970-01-01
  • 2016-02-06
相关资源
最近更新 更多