MHDev - 1 year ago 54

iOS Question

I have an array of

`routine`

`struct Routine {`

let routineName: String

let routineExercisesAndSets: [String: Int]

}

let routines: [Routine] = {

let firstRoutine = Routine(routineName: "Legs", routineExercisesAndSets: ["Squats":4,"Lunges":4,"Calf Raises":4])

let secondRoutine = Routine(routineName: "Chest", routineExercisesAndSets: ["Bench Press":4,"Press Ups":4,"Flyes":4,"Inclined Bench Press":4])

let thirdRoutine = Routine(routineName: "Arms", routineExercisesAndSets: ["Bicep Curls":4,"Hammer Curls":4,"Preacher Curls":4,"Tricep Extension":4,"Tricep Dips":4])

return [firstRoutine, secondRoutine, thirdRoutine]

}()

I am trying to return them

`routineExercisesAndSets`

`UITableViewCell`

My question is, how could I iterate through the routine objects and print out a 'pretty' formatted string that looks something like:

`"Calf Raises x 4, Squats x 4, Lunges x 4..."`

I can print out the individual key, value pairs on new lines using this code:

`for i in routines{`

for (key, value) in (i.routineExercisesAndSets)! {

print("\(key) x \(String(value))")

}

}

which produces:

`Calf Raises x 4`

Squats x 4

Lunges x 4

Bench Press x 4

Press Ups x 4

Flyes x 4

Inclined Bench Press x 4

Tricep Extension x 4

Tricep Dips x 4

Preacher Curls x 4

Hammer Curls x 4

Bicep Curls x 4

but I want to group them by their parent object so the exercises for a routine display on the same line so it looks like the example

`"Calf Raises x 4, Squats x 4, Lunges x 4..."`

Answer Source

You could make of a chained `map`

and `joined`

operation to construct a single `String`

describing the exercise dictionary for a given exercise. E.g.

```
for routine in routines {
print("Routine:", routine.routineName)
print("--------------")
print(routine.routineExercisesAndSets
.map { $0 + " x \($1)" }
.joined(separator: ", "))
print()
}
/* Routine: Legs
--------------
Calf Raises x 4, Squats x 4, Lunges x 4
Routine: Chest
--------------
Bench Press x 4, Press Ups x 4, Flyes x 4, Inclined Bench Press x 4
Routine: Arms
--------------
Tricep Extension x 4, Tricep Dips x 4, Preacher Curls x 4, Hammer Curls x 4, Bicep Curls x 4
*/
```

Furthermore, instead of explicitly performing these "presentation" conversions upon each use, you could implement them as the return of the computed `description`

property of `Routine`

, as a part of letting `Routine`

conform to `CustomStringConvertible`

:

```
extension Routine: CustomStringConvertible {
var description: String {
return routineName + ":\n" + routineExercisesAndSets
.map { $0 + " x \($1)" }
.joined(separator: ", ")
}
}
for routine in routines {
print(routine) /* uses Routine:s implementation of 'CustomStringConvertible',
the computed property 'description`, specifically */
print()
}
/* Legs:
Calf Raises x 4, Squats x 4, Lunges x 4
Chest:
Bench Press x 4, Press Ups x 4, Flyes x 4, Inclined Bench Press x 4
Arms:
Tricep Extension x 4, Tricep Dips x 4, Preacher Curls x 4, Hammer Curls x 4, Bicep Curls x 4
*/
```

Given your comment below, it seems as if the properties `routineName`

and `routineExercisesAndSets`

of `Routine`

are optionals. If this is the case, you naturally need to handle unwrapping them prior to accessing their (possibly existing) values. E.g., returning an empty `description`

if any of the two properties is `nil`

, and otherwise, the combined routine name and details description (as done in the non-optional examples above):

```
struct Routine {
let routineName: String?
let routineExercisesAndSets: [String: Int]?
}
extension Routine: CustomStringConvertible {
var description: String {
guard let name = routineName, let details = routineExercisesAndSets else { return "" }
return name + ":\n" + details.map { $0 + " x \($1)" }.joined(separator: ", ")
}
}
```