I'm working on an app which does a lot of real time computing based on values loaded in a struct using JSONDecoder(). I need to minimise computations in run time so I'd like to do some calculation up front. For this question I've created a working example with som fictional names for example purpose only
This piece of code loads data from a json. My quest is to use data from one part and inject it in another part based on name similarity of these parts.
struct Structure: Decodable {
static func withJSON(_ fileName: String) -> Structure? {
guard let url = Bundle.main.url(forResource: fileName, withExtension: "json", subdirectory: "Structures") else { return nil }
guard let data = try? Data(contentsOf: url) else { return nil }
do {
let decoded = try JSONDecoder().decode(Structure.self, from: data)
return decoded
}
catch{
print("Unexpected error Structure withJSON: \(error).")
}
return nil
}
private enum Keys: CodingKey {
case id
case rooms
case lists
}
let id: Int
let rooms: [Room]
let lists: [List]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Keys.self)
id = try container.decode(Int.self, forKey: .id)
rooms = try container.decode([Room].self, forKey: .rooms)
lists = try container.decode([List].self, forKey: .lists)
}
}
extension Structure {
struct Room: Decodable {
private enum RoomKeys: CodingKey {
case roomName
case dimension
case doors
}
let roomName: String
let dimension: [Double]
let doors: Int
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RoomKeys.self)
roomName = try container.decode(String.self, forKey: .roomName)
dimension = try container.decode([Double].self, forKey: .dimension)
doors = try container.decode(Int.self, forKey: .doors)
}
}
}
extension Structure {
struct List: Decodable {
private enum RoomKeys: CodingKey {
case roomName
case calculated
}
let roomName: String
var calculated: Double?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RoomKeys.self)
roomName = try container.decode(String.self, forKey: .roomName)
calculated = 0 // This variable should be filled with the array sum of Structure.Room.dimensions bonus for minus the doors of that (room * 2)
}
}
}
In this over simplified example I'd like to compute Structure.Lists.calculated with the sum of the array which sits in rooms.dimension with both the SAME roomName. See this json example:
{
"id": 1,
"rooms": [
{
"roomName": "living",
"dimension": [6,4,9],
"doors": 2
},
{
"roomName": "hall",
"dimension": [2,4,4],
"doors": 2
},
{
"roomName": "bathroom",
"dimension": [1,1,1],
"doors": 2
}
],
"lists": [
{
"roomName": "living"
},
{
"roomName": "bathroom"
}
]
}
In this example the optional Structure.List.calculated with the roomName "living" should be filled with the sum of dimension [6,4,9] thus 19 (bonus to subtract the doors * 2)
Can this be done on the fly while decoding? Or is this data not available yet since the struct is still loading? Then what approach should I take?
CodePudding user response:
When decoding your "Structure", you can process your raw decoded Lists
and store processed versions:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Keys.self)
id = try container.decode(Int.self, forKey: .id)
rooms = try container.decode([Room].self, forKey: .rooms)
let rawLists = try container.decode([List].self, forKey: .lists)
var listsWithSums: [List] = []
for var list in rawLists {
if let room = rooms.first(where: { $0.roomName == list.roomName }) {
list.calculated = room.dimension.reduce(0, )
- Double(room.doors)
}
listsWithSums.append(list)
}
lists = listsWithSums
}