I am trying to create a function that will sum json items from within separate nodes/ nested arrays, specifically the components.totalCalories data in the sample below. It is my understanding that I need to first get the data I want into a flat array and then use the .reduce modifier. I have tried the .map and .flatMap modifiers for this without any success so far. I have also tried using the .reduce within a ForEach loop in my view code to achieve the same result but again without any success.
One potential solution for this could be drilling down through the JSON with loops.
static func totalCalories(data:[TestData]) -> Int{
var calories = 0
for i in data{
for t in i.items{
for c in t.components{
calories += c.totalCalories
}
}
}
return calories
}
My question is why would you do this if you already have the totalCalories of the ingredients with in the parent object?
Thanks Lowgy, I’m get the ’ Type “” does not conform to protocol “Sequence” ’ error message, I will have a play around t see if I can overcome that.
The reason for needing this function is so that the totalCalories in the parent object will change its value depending on the user interactions. The user will be able to modify the quantity and variety of the components which will alter the totalCalories value.
Making several assumptions about how your JSON is being decoded, reduce would work something like this:
struct Menu: Decodable {
let catagory: String
let items: [Recipe]
}
struct Recipe: Decodable, Identifiable {
var id: String { name }
let name: String
let totalCalories: Int
let isFavourite: Bool
let addedToday: Int
let components: [RecipeComponent]
}
struct RecipeComponent: Decodable {
let name: String
let varietyName: String
let varietyCalories: Int
let totalCalories: Int
let quantity: Int
let unit: String
}
struct RecipeSumView: View {
@State private var recipes: [Recipe] = []
var totalCalories: Int {
return recipes.reduce(0) { cals, currentItem in
cals + currentItem.totalCalories
}
}
var body: some View {
VStack(spacing: 40) {
Text("Total Calories = \(totalCalories)")
VStack(spacing: 20) {
ForEach(recipes) { recipe in
Text(recipe.name).font(.headline)
Text(String(recipe.totalCalories))
}
}
}
.onAppear { loadJSON() }
}
func loadJSON() {
let decoder = JSONDecoder()
do {
let menu = try decoder.decode([Menu].self, from: menuJSON)
recipes = menu[0].items
} catch {
print(error)
}
}
}
The only change I had to make was to change the computed var to; @State private var recipes = Recipe
var totalCalories: Int {
return recipes.RecipeComponent.reduce(0) { cals, currentItem in
cals + currentItem.totalCalories
}
}