Multilevel Array Appending

I'm working on a workout app which has an array within an array and takes user input to populate each array.

In the first array, I can add an item to plans using the addPlan function. However, I cannot seem to figure out how to add items to the next level in the addExercise function.

final class ViewModel: ObservableObject {
    @Published var plans: [Plan] = []
    
    func addPlan(_ item: String) {
        let plan = Plan(name: item, exercise: [])
        plans.insert(plan, at: 0)
    }
    func addExercise() {
        
    }
}

Essentially, Plan holds an array of Exercise. Here is the model for Plan

struct Plan: Identifiable {
    let id = UUID()
    let name: String
    var exercise: [Exercise]
    
    static let samplePlans = Plan(name: "Chest", exercise: [Exercise(name: "Bench"), Exercise(name: "Incline Bench")])
}

These two functions should behave the same but just appending the items into different arrays.

The end result would be:

Plan 1: [Exercise1, Exercise2, Exercise3]

Plan 2: [Exercise4, Exercise5, Exercise6] etc.

Project files: LINK

Answered by Claude31 in 754685022

What JaiRajput said, plus.

When you add an exercise, you give it a name.

So code should be:

final class ViewModel: ObservableObject {
    @Published var plans: [Plan] = []
    
    func addPlan(_ item: String) {
        let plan = Plan(name: item, exercise: [])
        plans.insert(plan, at: 0)
    }

    func addExercise(to planIndex: Int, exoName: String) {
        if exoName == ""  { return }
        if planIndex < 0 || planIndex >= plans.count { return }

        // Also test exoName does not exist there already
        for exo in plans[planIndex].exercises {
            if exo.name == exoName { return }
        }
        // All ok, add to exercises. append will add as last exercise, insert(at: 0) would append as first.
        plans[planIndex].exercises.append(Exercise(name: exoName))
    }
}

You add an exercise to a plan.

So, in addExercise, you should add a plan reference:

func addExercise(forPlan: Plan)

and then

forPlan.insert() // What you need to insert

I was not precise enough. Correction is straightforward.

forPlan.exercise.insert() // What you need to insert

Note: you'd better name it exercises and not exercise.

    @Published var plans: [Plan] = []
    
    func addPlan(_ item: String) {
        let plan = Plan(name: item, exercises: [])
        plans.insert(plan, at: 0)
    }
    
    func addExercise(to planIndex: Int, exercise: Exercise) {
        guard planIndex >= 0 && planIndex < plans.count else {
            return 
        }
        
        plans[planIndex].exercises.append(exercise) 
    }
}

check this...

Accepted Answer

What JaiRajput said, plus.

When you add an exercise, you give it a name.

So code should be:

final class ViewModel: ObservableObject {
    @Published var plans: [Plan] = []
    
    func addPlan(_ item: String) {
        let plan = Plan(name: item, exercise: [])
        plans.insert(plan, at: 0)
    }

    func addExercise(to planIndex: Int, exoName: String) {
        if exoName == ""  { return }
        if planIndex < 0 || planIndex >= plans.count { return }

        // Also test exoName does not exist there already
        for exo in plans[planIndex].exercises {
            if exo.name == exoName { return }
        }
        // All ok, add to exercises. append will add as last exercise, insert(at: 0) would append as first.
        plans[planIndex].exercises.append(Exercise(name: exoName))
    }
}

That will be a bit difficult to use id, as you do not know directly what it is.

You have 2 options:

  • create a planIndex
  • identify plan by its name, as it must be unique.

Second solution is the best.

Code will become:

  • to make sure name is unique:
    func addPlan(_ item: String) {
        if item == "" { return }
        // Also test item does not exist there already
        for plan in plans {
            if item == plan.name { return }
        }
        // now we can add safely
        let plan = Plan(name: item, exercises: [])
        plans.insert(plan, at: 0)
    }
  • use name to identify plan
    func addExercise(to planName: String, exoName: String) {
        if exoName == ""  { return }

        // find the index, which is unique
        guard let planIndex = plans.firstIndex(of: planName) else { return }

        // Also test exoName does not already exist in this plan
        for exo in plans[planIndex].exercises {
            if exo.name == exoName { return }
        }
        // All ok, add to exercises. append will add as last exercise, insert(at: 0) would append as first.
        plans[planIndex].exercises.append(Exercise(name: exoName))
    }
}

This should now work properly. If not, explain. If OK, don't forget to close the thread.

Exact.

We must work on Strings:

let plansNames = plans.map { $0.name }
guard let planIndex = plansNames.firstIndex(of: planName) else { return }
Multilevel Array Appending
 
 
Q