Создание предсказуемой структуры данных с помощью вложенного словаря, не прибегая к Any

У меня есть следующий словарь:

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.

Не используйте Dictionary для представления данных в коде. Используйте настоящие структуры данных — пользовательские struct идеально подходят для моделей данных. Если вам нужно отправить их по сети или преобразовать в JSON по какой-либо другой причине, сделайте ваши типы соответствующими Codable и используйте JSONEncoder/JSONDecoder.

Dávid Pásztor 04.04.2023 16:01

@DávidPásztor Это мой вопрос. Я специально изо всех сил пытаюсь превратить это представление в пользовательскую структуру данных. Вот с этим и прошу помощи.

user137369 04.04.2023 16:03

Наилучшая структура данных для использования зависит от того, как вы будете использовать объект. Вы будете искать данные по ключам (первый, второй, третий). Нужно ли сохранять порядок ключей (ваш текущий словарь этого не делает, так что может и нет). Будете ли вы преобразовывать это в/из JSON? Пожалуйста, предоставьте больше контекста.

Geoff Hackworth 04.04.2023 16:08

@GeoffHackworth Чтобы уточнить, я (думаю, что я) забочусь о том, чтобы каждая клавиша работала как struct (или что-то еще), а не словарь в целом. Я буду искать данные по ключам. Порядок не имеет значения. Я конвертирую его в JSON, но не как есть: он будет включен в другие ключи (хотя эта часть работает; словарь как есть работает).

user137369 04.04.2023 16:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
62
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Если структура содержимого словаря фиксирована, вам нужно отслеживать только два значения для каждого элемента в верхнем словаре. Таким образом, структура может быть определена как

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}

Это возвращает массив словарей вместо плоского словаря.

user137369 04.04.2023 17:28

«Это» было просто примером сопоставления отдельных элементов. Если вам нужен плоский словарь, вы можете легко использовать reduce(into:)

Joakim Danielson 04.04.2023 17:59

Сильно вдохновленный решением Йоакима, но принимая во внимание требование искать сообщения по их ключу, я придумал следующее:

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)!)

Другие вопросы по теме