GarySabo
GarySabo

Reputation: 6710

How best to organize objects into month and year in Swift?

Working in HealthKit, I have an array of HealthKit Workouts that I need to organize into month and year (so that I can display workouts from Jan 2018, Feb 2018 etc.). What makes it difficult in my mind is I first need to check if there is a workout for a given month and year, if not I need to create the array for it, if there is I need to append to the existing array. I also am unsure of the best data model, I was thinking of using a [[Month:Year]] but that doesn't seem very Swifty?

guard let workoutsUnwrapped = workouts else { return }

for workout in workoutsUnwrapped {
    let calendar = Calendar.current
    let year = calendar.component(.year, from: workout.startDate)
    let month = calendar.component(.month, from: workout.startDate)
}

Upvotes: 2

Views: 978

Answers (1)

rmaddy
rmaddy

Reputation: 318884

I'd start by creating a struct to hold a year and month:

struct YearMonth: Comparable, Hashable {
    let year: Int
    let month: Int

    init(year: Int, month: Int) {
        self.year = year
        self.month = month
    }

    init(date: Date) {
        let comps = Calendar.current.dateComponents([.year, .month], from: date)
        self.year = comps.year!
        self.month = comps.month!
    }

    var hashValue: Int {
        return year * 12 + month
    }

    static func == (lhs: YearMonth, rhs: YearMonth) -> Bool {
        return lhs.year == rhs.year && lhs.month == rhs.month
    }

    static func < (lhs: YearMonth, rhs: YearMonth) -> Bool {
        if lhs.year != rhs.year {
            return lhs.year < rhs.year
        } else {
            return lhs.month < rhs.month
        }
    }
}

Now you can use this as a key in a dictionary where each value is an array of workouts.

var data = [YearMonth: [HKWorkout]]()

Now iterate your workouts:

guard let workouts = workouts else { return }

for workout in workouts {
    let yearMonth = YearMonth(date: workout.startDate)
    var yearMonthWorkouts = data[yearMonth, default: [HKWorkout]())
    yearMonthWorkouts.append(workout)
    data[yearMonth] = yearMonthWorkouts
}

Once this is done, all of your workouts are grouped by year/month.

You can build a sorted list of year/months for the keys in the dictionary.

let sorted = data.keys.sorted()

To apply this to a table view, use sorted to define the number of sections. For each section, get the array of workouts from data for the given YearMonth of the corresponding section.

Upvotes: 7

Related Questions