Home > Software design >  how to get data based on the previous level data
how to get data based on the previous level data

Time:12-14

I have JSON as below.

{
  "type": "regular",
  "data": [
    {
      "title": "Title 1",
      "price_regular": 1.1,
      "price_express": 2.2,
    },
    {
      "title": "Title 2",
      "price_regular": 1.1,
      "price_express": 2.2,
    }
  ]
}

For this I have model as below.

struct MainModel : Codable {
    var type : String?
    var data : [DataModel]?
}

struct DataModel : Codable {
    var title : String?
    var price_regular : Float?
    var price_express : Float?
    
    var price : Float {
        get {
            if (type == "regular") { ----> Please check below query for this.
                return price_regular ?? 0.0
            }
            
            return price_express ?? 0.0
        }
    }
}

What I am doing in DataModel is create new variable as price. I want to check what data I have for type of main class and based on that I want to get value for Price. Is there way I can achieve this?

Actually I am playing in model. I know I can make this run-time, but I would like to do this in model so that logic is at 1 place only.

CodePudding user response:

I'd do it with a custom init(from decoder:), allowing to pass type from MainModel to DataModel.

struct MainModel : Codable {
    var type : String?
    var data : [DataModel]?

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        type = try container.decode(String.self, forKey: .type)
        var dataContainer = try container.nestedUnkeyedContainer(forKey: .data)
        var dataTemp: [DataModel] = []
        while !dataContainer.isAtEnd {
            let dataSubcontainer = try dataContainer.nestedContainer(keyedBy: DataModel.DataModelKeys.self)
            let title = try dataSubcontainer.decode(String.self, forKey: .title)
            let price_regular = try dataSubcontainer.decode(Float.self, forKey: .price_regular)
            let price_express = try dataSubcontainer.decode(Float.self, forKey: .price_express)
            dataTemp.append(DataModel(title: title, price_regular: price_regular, price_express: price_express, type: type))
        }
        data = dataTemp
    }
}

struct DataModel : Codable {
    var title : String?
    var price_regular : Float?
    var price_express : Float?
    var type: String?

    enum DataModelKeys: String, CodingKey {
        case title
        case price_regular
        case price_express
    }

    var price : Float {
        get {
            if (type == "regular") {
                return price_regular ?? 0.0
            }
            return price_express ?? 0.0
        }
    }
}

To test it:

let json = """
[{
"type": "regular",
"data": [
{
  "title": "Title 1",
  "price_regular": 1.1,
  "price_express": 1.2,
},
{
  "title": "Title 2",
  "price_regular": 2.1,
  "price_express": 2.2,
}
]
},
    {
"type": "irregular",
"data": [
{
  "title": "Title 3",
  "price_regular": 1.1,
  "price_express": 1.2,
},
{
  "title": "Title 4",
  "price_regular": 2.1,
  "price_express": 2.2,
}
]
}]
"""

do {
    let model = try JSONDecoder().decode([MainModel].self, from: Data(json.utf8))
    print(model)
    model.forEach { aMainModel in
        print("MainModel type: \(aMainModel.type)")
        aMainModel.data?.forEach {
            print("Title: \($0.title) - type: \($0.type) - price: \($0.price)")
        }
    }
} catch {
    print("Error: \(error)")
}

Which output:

$>MainModel type: Optional("regular")
$>Title: Optional("Title 1") - type: Optional("regular") - price: 1.1
$>Title: Optional("Title 2") - type: Optional("regular") - price: 2.1
$>MainModel type: Optional("irregular")
$>Title: Optional("Title 3") - type: Optional("irregular") - price: 1.2
$>Title: Optional("Title 4") - type: Optional("irregular") - price: 2.2

CodePudding user response:

With help of @Larme answer, I updated model and made it working.

struct MainModel : Codable {
    var type : String?
    var data : [DataModel]? {
        didSet { // on didSet assigned value of type to type
            for i in 0..<(self.data ?? [DataModel]()).count {
                self.data![i].type = self.type ?? ""
            }
        }
    }
    
    
}

struct DataModel : Codable {
    var title : String?
    var price_regular : Float?
    var price_express : Float?
    
    var type : String? // added new variable
    
    var price : Float? {
        get {
            if (type == "regular") {
                return price_regular ?? 0.0
            }
            return price_express ?? 0.0
        }
    }
}

And this helped me of doing what I was looking for...

  • Related