#swift #dictionary #enums
#swift #словарь #перечисления
Вопрос:
Я хотел бы преобразовать значение, которое я получаю из API, в определенный формат.
[String:Any] // format received
[Int:[ContentType:Int]] // required format
ContentType — это перечисление
Пример данных может выглядеть следующим образом:
["123":["Tables":"25","Chairs":"14"]] // input
[123:[.Tables:25,.Chairs:14]] // output
Я думаю, что мне нужно иметь карту внутри карты, чтобы это работало, но я изо всех сил пытаюсь выработать способ продвижения вперед. Хотя, возможно, я лаю совершенно не на то дерево. На самом деле я не хочу вручную перебирать и добавлять каждый элемент по одному; я ищу что-то более интеллектуальное, чем это, если это возможно.
enum ContentType: String {
case Tables,Chairs
}
let original_values: [String:Any]
= ["1234":["Tables":"5","Chairs":"2"]]
let values: [Int:[ContentType:Int]]
= Dictionary(uniqueKeysWithValues: original_values.map {
(
Int($0.key)!,
(($0.value as? [String:String]).map { // Error on this line - expects 1 argument but two were used
(
ContentType(rawValue: $1.key)!, // $1 is presumably wrong here?
Int($1.value)
)
}) as? [ContentType:Int]
)
})
У кого-нибудь есть идеи?
Ответ №1:
Я хотел бы преобразовать значение, которое я получаю из API, в определенный формат.
Вы можете создать свое перечисление Decodable
enum ContentType: String, Decodable {
case tables, chairs
enum CodingKeys: String, CodingKey {
case Tables = "Tables"
case Chairs = "Chairs"
}
}
Затем вы можете декодировать полученное, Data
а затем compactMap
его отформатировать (Int, [ContentType: Int])
. Эти кортежи вы можете преобразовать в Dictionary
с помощью разработанного инициализатора
do {
let decoded = try JSONDecoder().decode([String: [ContentType: Int]].self, from: data)
let mapped = Dictionary(uniqueKeysWithValues: decoded.compactMap { (key,value) -> (Int, [ContentType: Int])? in
if let int = Int(key) {
return (int, value)
} else {
return nil
}
})
} catch {
print(error)
}
Комментарии:
1. Это не ответ на вопрос OP. Но огромный плюс для декодируемости. Но почему вы
[String: ...]
вместо[Int: ...]
декодируете? Проблема с decodable заключается в том, что когда хотя бы одно целое число где-то внутри будет неправильным, тогда полное декодирование завершится ошибкой.2. @ManWithBear Я декодирую
[String: ...]
и переназначаю его, потому что ключи в словаре из ответаString
нетInt
. Также для вашего последнего предложения: этого не произойдет, потому что я используюcompactMap
3. в таком случае вам нужно расшифровать в
[String: [ContentType: String]]
, а затем также отобразить значение внутри значения.
Ответ №2:
В этой строке:
(($0.value as? [String:String]).map {
Вы используете не Sequence.map
, а Optional.map
.
Рабочее решение:
/// First let's map plain types to our types
let resultArray = original_values
.compactMap { (key, value) -> (Int, [ContentType: Int])? in
guard let iKey = Int(key), let dValue = value as? [String: String] else { return nil }
let contentValue = dValue.compactMap { (key, value) -> (ContentType, Int)? in
guard let cKey = ContentType(rawValue: key), let iValue = Int(value) else { return nil }
return (cKey, iValue)
}
let contentDict = Dictionary(uniqueKeysWithValues: contentValue)
return (iKey, contentDict)
}
let result = Dictionary(uniqueKeysWithValues: resultArray)
Для улучшения вывода на печать добавьте соответствие CustomStringConvertible
:
extension ContentType: CustomStringConvertible {
var description: String {
switch self {
case .Tables:
return "Tables"
case .Chairs:
return "Chairs"
}
}
}
Комментарии:
1. Мне действительно нравится этот подход, спасибо! К сожалению, я получаю
[1234: [__lldb_expr_19.ContentType.Chairs: 2, __lldb_expr_19.ContentType.Tables: 5]]
на игровой площадке — что бы это ни значило…2. @Ben, по-моему, все в порядке
3. @Ben это правильно. Игровая площадка скомпилирует ваш код во внутреннем модуле, чтобы вы получили эти дополнительные префиксы для перечисления
Ответ №3:
Это правильный синтаксис Swift 5
enum ContentType: String {
case tables = "Tables"
case chairs = "Chairs"
}
let originalValues: [String: [String: String]]
= ["1234": ["Tables": "5", "Chairs": "2"]]
let values: [Int: [ContentType: Int]] = Dictionary(uniqueKeysWithValues:
originalValues.map { arg in
let (key, innerDict) = arg
let outMap: [ContentType: Int] = Dictionary(uniqueKeysWithValues:
innerDict.map { innerArg in
let (innerKey, innerValue) = innerArg
return (ContentType.init(rawValue: innerKey)!, Int(innerValue)!)
}
)
return (Int(key)!, outMap)
}
)
print(values)
[1234: [__lldb_expr_5.ContentType.tables: 5, __lldb_expr_5.ContentType.chairs: 2]]
Комментарии:
1. Вы никогда не должны использовать явно развернутые опции при работе с внутренними данными
2. Это всего лишь пример игровой площадки