简体   繁体   中英

How to print an array of dictionary values into a nice string in Swift

I have an array of routine objects thats are defined as:

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 array into a UITableViewCell as detail text and when using the array.description function I get this result in the tableView

这个结果在tableView中

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..." .

You could make of a chained map and joined operation to construct a single String describing the exercise dictionary for a given exercise. Eg

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. Eg, 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: ", ")
    }
}

First of all let's conform Routine to CustomStringConvertible

extension Routine: CustomStringConvertible {
    var description: String {
        return routineExercisesAndSets.map { $0 + " x " + $1.description }.joined(separator: ", ")
    }
}

Now you can easily write

routines.forEach { print($0) }

Result

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

Or given a UITableViewCell ...

let cell: UITableViewCell = ...
let routine: Routine = ...
cell.detailTextLabel?.text = routine.description

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM