Как декодировать динамические ключи с помощью префикса

Я хочу декодировать strIngredient1, strIngredient2, strIngredient3 в массив, а strMeasure1, strMeasure2, strMeasure3 в отдельный массив. Как мы можем это сделать, используя декодируемые и кодирующие ключи. У меня есть объект, соответствующий декодируемому протоколу, и функция public init (из декодера: Decoder).

{
  "meals": [
    {
      "idMeal": "53049",
      "strMeal": "Apam balik",
      "strDrinkAlternate": null,
      "strCategory": "Dessert",
      "strArea": "Malaysian",
      "strInstructions": "Mix milk, oil and egg together. Sift flour, baking powder and salt into the mixture. Stir well until all ingredients are combined evenly.\r\n\r\nSpread some batter onto the pan. Spread a thin layer of batter to the side of the pan. Cover the pan for 30-60 seconds until small air bubbles appear.\r\n\r\nAdd butter, cream corn, crushed peanuts and sugar onto the pancake. Fold the pancake into half once the bottom surface is browned.\r\n\r\nCut into wedges and best eaten when it is warm.",
      "strMealThumb": "https://www.themealdb.com/images/media/meals/adxcbq1619787919.jpg",
      "strTags": null,
      "strYoutube": "https://thewikihow.com/video_6R8ffRRJcrg",
      "strIngredient1": "Milk",
      "strIngredient2": "Oil",
      "strIngredient3": "Eggs",
      "strIngredient4": "Flour",
      "strIngredient5": "Baking Powder",
      "strIngredient6": "Salt",
      "strIngredient7": "Unsalted Butter",
      "strIngredient8": "Sugar",
      "strIngredient9": "Peanut Butter",
      "strIngredient10": "",
      "strIngredient11": "",
      "strIngredient12": "",
      "strIngredient13": "",
      "strIngredient14": "",
      "strIngredient15": "",
      "strIngredient16": "",
      "strIngredient17": "",
      "strIngredient18": "",
      "strIngredient19": "",
      "strIngredient20": "",
      "strMeasure1": "200ml",
      "strMeasure2": "60ml",
      "strMeasure3": "2",
      "strMeasure4": "1600g",
      "strMeasure5": "3 tsp",
      "strMeasure6": "1/2 tsp",
      "strMeasure7": "25g",
      "strMeasure8": "45g",
      "strMeasure9": "3 tbs",
      "strMeasure10": " ",
      "strMeasure11": " ",
      "strMeasure12": " ",
      "strMeasure13": " ",
      "strMeasure14": " ",
      "strMeasure15": " ",
      "strMeasure16": " ",
      "strMeasure17": " ",
      "strMeasure18": " ",
      "strMeasure19": " ",
      "strMeasure20": " ",
      "strSource": "https://www.nyonyacooking.com/recipes/apam-balik~SJ5WuvsDf9WQ",
      "strImageSource": null,
      "strCreativeCommonsConfirmed": null,
      "dateModified": null
    }
  ]
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
0
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Вот что бы сделал я, который немного отличается от мнения комментатора. Я бы начал с создания собственной реализации CodingKey для двух пронумерованных полей:

enum MealNumberedFieldKey {
    case ingredients(Int)
    case measurements(Int)
}

extension MealNumberedFieldKey: CodingKey {
    var intValue: Int? { nil }

    init?(intValue: Int) {
        return nil
    }

    init?(stringValue: String) {
        var string = stringValue
        if string.hasPrefix("strIngredient") {
            string.removeFirst(13)
            guard let number = Int(string) else { return nil }
            self = .ingredients(number)
        } else if string.hasPrefix("strMeasure") {
            string.removeFirst(10)
            guard let number = Int(string) else { return nil }
            self = .measurements(number)
        } else {
            return nil
        }
    }

    var stringValue: String {
        switch self {
        case .ingredients(let number):
            return "strIngredient\(number)"
        case .measurements(let number):
            return "strMeasure\(number)"
        }
    }
}

Затем я бы написал специальную функцию init(from decoder:), используя два контейнера декодирования: один с обычными ключами кодирования, а другой с пользовательскими ключами кодирования:

struct Meal: Decodable {
    let idMeal: String
    let strMeal: String
    // ...
    let ingredients: [String]
    let measurements: [String]

    enum CodingKeys: String, CodingKey {
        case idMeal
        case strMeal
    }

    init(from decoder: any Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.idMeal = try container.decode(String.self, forKey: .idMeal)
        self.strMeal = try container.decode(String.self, forKey: .strMeal)
        // ...

        let numberedContainer = try decoder.container(keyedBy: MealNumberedFieldKey.self)
        self.ingredients = try (1...20).map { number in
            try numberedContainer.decode(String.self, forKey: .ingredients(number))
        }
        self.measurements = try (1...20).map { number in
            try numberedContainer.decode(String.self, forKey: .measurements(number))
        }
    }
}

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