У меня есть следующий словарь:
let myDict: [String: Any] = [
"first": [
"message": "qqq",
"extras": ["task": "first"]
],
"second": [
"message": "www",
"extras": ["task": "second"]
],
"third": [
"message": "eee",
"extras": ["task": "third"]
],
]
Структура предсказуема: верхние ключи (first
, second
, third
) могут иметь любое имя, но они всегда содержат одну message
строку и один extras
словарь, который, в свою очередь, содержит один task
, значением которого является исходный верхний ключ. Структура является фиксированной, поскольку она влияет на другие части системы.
Сейчас myDict
— это [String: Any]
, но кажется, что это можно улучшить, поскольку известны все типы. Тем не менее, я изо всех сил пытаюсь придумать решение для этого, до такой степени, что сомневаюсь, подходит ли struct
?
Количество ключей в словаре варьируется, поэтому, может быть, каждый из них должен быть struct
, а не словарь в целом? Я не уверен.
На данные будут ссылаться ключи, а их содержимое затем будет включено в другие ключи, создающие JSON. Эта часть работает, мне не нужна помощь для создания JSON.
@DávidPásztor Это мой вопрос. Я специально изо всех сил пытаюсь превратить это представление в пользовательскую структуру данных. Вот с этим и прошу помощи.
Наилучшая структура данных для использования зависит от того, как вы будете использовать объект. Вы будете искать данные по ключам (первый, второй, третий). Нужно ли сохранять порядок ключей (ваш текущий словарь этого не делает, так что может и нет). Будете ли вы преобразовывать это в/из JSON? Пожалуйста, предоставьте больше контекста.
@GeoffHackworth Чтобы уточнить, я (думаю, что я) забочусь о том, чтобы каждая клавиша работала как struct
(или что-то еще), а не словарь в целом. Я буду искать данные по ключам. Порядок не имеет значения. Я конвертирую его в JSON, но не как есть: он будет включен в другие ключи (хотя эта часть работает; словарь как есть работает).
Если структура содержимого словаря фиксирована, вам нужно отслеживать только два значения для каждого элемента в верхнем словаре. Таким образом, структура может быть определена как
struct Message {
let message: String
let key: String
}
Если вам нужно преобразовать его в словарь, вы можете добавить вычисляемое свойство:
var dictionaryRepresentation: [String: Any] {
[key: ["message": message, "extras": ["task": key]] as [String : Any]]
}
Пример
let array = [Message(message: "qqq", key: "first"), Message(message: "www", key: "second"), Message(message: "eee", key: "third")]
print(array.map(\.dictionaryRepresentation))
[["первый": ["дополнительно": ["задача": "первый"], "сообщение": "qqq"]], ["второй": ["сообщение": "www", "дополнительно": [ "задача": "вторая"]]], ["третья": ["сообщение": "ееее", "дополнительно": ["задача": "третья"]]]]
Чтобы полностью воссоздать словарь в вопросе, используя пример массива выше, можно использовать reduce(into:)
array.reduce(into: [:]) { $0[$1.key] = $1.dictionaryRepresentation}
Это возвращает массив словарей вместо плоского словаря.
«Это» было просто примером сопоставления отдельных элементов. Если вам нужен плоский словарь, вы можете легко использовать reduce(into:)
Сильно вдохновленный решением Йоакима, но принимая во внимание требование искать сообщения по их ключу, я придумал следующее:
struct Message {
let message: String
let key: String
}
extension Message {
var dictionaryRepresentation: [String: Any] {
["message": message, "extras": ["task": key] as [String: Any]]
}
}
struct Messages {
private var messages = [String: Message]()
mutating func add(_ message: String, withKey key: String) {
messages[key] = Message(message: message, key: key)
}
func message(for key: String) -> Message? {
messages[key]
}
}
extension Messages {
var dictionaryRepresentation: [String: Any] {
messages.mapValues(\.dictionaryRepresentation)
}
}
Вы создаете свой словарь сообщений, добавляя сообщения:
var myMessages = Messages()
myMessages.add("qqq", withKey: "first")
myMessages.add("www", withKey: "second")
myMessages.add("eee", withKey: "third")
Вы можете просмотреть сообщение:
if let secondMessage = myMessages.message(for: "second") {
print("second message is \(secondMessage)")
print("second message as dictionary is \(secondMessage.dictionaryRepresentation)")
}
второе сообщение - это сообщение (сообщение: «www», ключ: «второе»)
второе сообщение в виде словаря ["message": "www", "extras": ["task": "second"]]
И вы можете получить словарное представление всех сообщений:
print(myMessages.dictionaryRepresentation)
["второе": ["сообщение": "www", "дополнительно": ["задача": "второе"]], "третье": ["дополнение": ["задача": "третье"], "сообщение ": "ееее"], "первый": ["сообщение": "qqq", "дополнительно": ["задача": "первый"]]]
Вы можете расширить Messages
с помощью инициализатора, который принимает массив ключей и сообщений или словарь строк ключ:сообщение.
Основываясь на ответе Йоакима Даниэльсона, альтернатива, которая избегает Any
:
struct Message: Codable {
let key: String
let details: Details
init(key: String, message: String) {
self.key = key
self.details = Details(message: message, extras: ["task": key])
}
struct Details: Codable {
let message: String
let extras: [String: String]
}
}
let array = [Message(key: "first", message: "qqq"), Message(key: "second", message: "www"), Message(key: "third", message: "eee")]
let dict = array.reduce(into: [:]) { $0[$1.key] = $1.details }
Чтобы преобразовать его в JSON:
import Foundation
let data = try JSONEncoder().encode(dict)
print(String(data: data, encoding: .utf8)!)
Не используйте
Dictionary
для представления данных в коде. Используйте настоящие структуры данных — пользовательскиеstruct
идеально подходят для моделей данных. Если вам нужно отправить их по сети или преобразовать в JSON по какой-либо другой причине, сделайте ваши типы соответствующимиCodable
и используйтеJSONEncoder
/JSONDecoder
.