Reputation: 559
I have an array of objects where each object has an Exercise Name and a random number of reps.
I then have a function to generate a random workout (with between 3 - 6 exercises in it)
However, when I print it the reps are almost always 1, 2 or occasionally 14, despite loading it 30 times or so.
Am I doing something wrong here?
Here's my objects and struct :
struct exerciseInWorkout {
let name : String
let reps : Int
}
let exerciseBankArray = [
exerciseInWorkout(name: "Squat", reps: (Int(arc4random_uniform(10)))),
exerciseInWorkout(name: "Push Ups", reps: (Int(arc4random_uniform(5)))),
exerciseInWorkout(name: "Viking Press", reps: (Int(arc4random_uniform(20)))),
]
and here's my function :
func generateWorkout(){
let possibleExercises = exerciseBankArray
let numberOfExercisesKey = Int(arc4random_uniform(4) + 3)
let workoutSet : [exerciseInWorkout] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(possibleExercises.count)))
return exerciseInWorkout(name: exerciseBankArray[randomKey].name, reps: exerciseBankArray[randomKey].reps)}
print (workoutSet)
}
}
Also, is there a way to create a set of these to avoid the same exercise coming up twice? I tried using Set but didn't seem to work at all.
Lastly, when I print it, each object is prepended with "project.exerciseInWorkout... is there any way to just print/return the clean array i.e. [["name: "press ups", reps: 12], [name:xyz, reps: 30]]?
Reason being I want to pass this to a new VC to put in a table view next and presume I need a clean array to do that.
Upvotes: 0
Views: 69
Reputation: 1591
It looks like your array exerciseBankArray
is stored globally, which means exerciseInWorkout
components are initialized once for the whole app. Having said that, it is then normal that the number of reps is always the same, the arc4random_uniform
is only executed on the first access.
If you want to keep the same structure, I recommend this
remove the arc4random_uniform
from exerciseBankArray
and just write the maximum amount of reps you want
let exerciseBankArray = [
exerciseInWorkout(name: "Squat", maxReps: 10),
exerciseInWorkout(name: "Push Ups", maxReps: 5),
exerciseInWorkout(name: "Viking Press", maxReps: 20)
]
Call the random function inside generateWorkout()
like this
func generateWorkout(){
let possibleExercises = exerciseBankArray
let numberOfExercisesKey = Int(arc4random_uniform(4) + 3)
let workoutSet : [exerciseInWorkout] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(possibleExercises.count)))
return exerciseInWorkout(
name: exerciseBankArray[randomKey].name,
reps: Int(arc4random_uniform(exerciseBankArray[randomKey].maxReps))
)
}
}
If you're open to making a better architecture for your code, here are some suggestions
Split your model for exercise into two classes / structs:
One that represents an actual exercise
struct WorkoutExercise {
let name: String
let reps: Int
}
One that represents a generator of exercise
struct WorkoutExerciseGenerator {
let name: String
let maxReps: Int
// Add any other parameter you need to generate a good exercise
func generate() -> WorkoutExercise {
return WorkoutExercise(
name: name,
reps: Int(arc4random_uniform(maxReps))
)
}
}
When you say remove global variables do you mean store the array of exercises in each VC that needs them? I just thought that would be “repeating myself” (from DRY principles etc?)
I totally agree with the DRY guidelines, but there are many ways to not repeat yourself. The issue with global variables (a variable that is not inside any class, just free-floating) are numerous:
Also, if I change to the 2nd example above, how would I then call the right amount of those? Just replace “return exerciseInWorkout” with the new function? Or would it be unchanged because the func is contained in the struct?
So I understand correctly, what you actually want to create is a set of default generators for exercises that have a name and a max count of reps, and these should be available in the whole project (hence why you used global variables).
A good way to improve this code is by defining static generators, for instance you can update WorkoutExerciseGenerator
to be
struct WorkoutExerciseGenerator {
let name: String
let maxReps: Int
// Add any other parameter you need to generate a good exercise
func generate() -> WorkoutExercise {
return WorkoutExercise(
name: name,
reps: Int(arc4random_uniform(maxReps))
)
}
// Generates a "Squat" workout
static var squat {
return WorkoutExerciseGenerator(name: "Squat", maxReps: 10)
}
// Generates a "Push Up" workout
static var pushUp {
return WorkoutExerciseGenerator(name: "Push Ups", maxReps: 5)
}
// Generates a "Viking Press" workout
static var vikingPress {
return WorkoutExerciseGenerator(name: "Viking Press", maxReps: 20)
}
}
Now that you have these specific generators, it looks like you also want to have a way to generate a whole workout. If that's the case, then you can simply create, in addition to what I wrote about, some objects to represent a workout and a workout generator.
/// This represents a whole workout that contains
/// multiple exercises
struct Workout {
let exercises: [WorkoutExercise]
}
/// This allows to dynamically creates a Workout
struct WorkoutGenerator {
// This is the "pool" of exercises from
// which it generates a workout (similar to your
// `exerciseBankArray`)
let exercisePool: [ExerciseGenerators]
// Min and max amount of workouts
let minCount: Int
let maxCount: Int
// Generates a workout from the generator
func generate() -> WorkoutGenerator {
let amount = Int(arc4random_uniform(maxCount - minCount)) + minCount
let exercises = (0..<amount).map { _ in
// Selects an exercise generator at random
let index = Int(arc4random_uniform(exercisePool.count))
// Generates a random workout exercise from this generator
return exercisePool[index].generate()
}
return Workout(exercises: exercises)
}
// Same thing here, you can use a static variable to create
// a "default" workout generator that contains the exercises
// you had inside your `exerciseBankArray`
static var default: WorkoutGenerator {
return WorkoutGenerator(
exercisePool: [.squat, .pushUp, .vikingPress],
minCount: 3,
maxCount: 6
)
}
}
Now that you have all of this, the only thing you need to do to create a totally random work-out according to your requirements is
let myWorkout = WorkoutGenerator.default.generate()
If you want to add more exercise types, just create more static ExerciseGenerator
, and if you want to create different types of workouts (maybe with different exercise pools, some hard or some easy), just create additional static WorkoutGenerator
. (Note that you don't need static, you can also just create the object directly in your VC).
Hope that helps!
Upvotes: 3