Esat kemal Ekren
Esat kemal Ekren

Reputation: 556

How can I group fetched dates by Month

I inserted data into core-data with a type of Date but when I try to fetch data's from the database I couldn't group them by month. You can find the code that I tried for a solution but it didn't work.

let groupedDict = Dictionary(grouping: self.lessons) { (month) -> Date in
        return month.lessonDate!
    }
    var groupedMonth = [[Lessons]]()
    let keys = groupedDict.keys.sorted()
    keys.forEach { (key) in
        groupedMonth.append(groupedDict[key]!)
    }
    groupedMonth.forEach ({
        $0.forEach({print($0)})
        print("--------")
    })

If I should give you an example of what I want to accomplish. Here is the example:

Sample Dates:

I wanted to group these data like

Thank you for helping me

Upvotes: 0

Views: 727

Answers (2)

Fabian
Fabian

Reputation: 5358

You have to use Dictionary(grouping:by:) differently. You have to return the value you want to group by, not for every lesson a different group.

Helpers

struct Lesson {
    let lessonDate: Date
}

extension Date {
    func addDays(_ days: Int) -> Date {
        return Calendar.current.date(byAdding: .day, value: days, to: self)!
    }

    func addMonths(_ months: Int) -> Date {
        return Calendar.current.date(byAdding: .month, value: months, to: self)!
    }

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

    var prettyMonth: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMMM yyyy"
        formatter.locale = Calendar.current.locale!

        return formatter.string(from: self)
    }

    var prettyDate: String {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .medium
        formatter.locale = Calendar.current.locale!

        return formatter.string(from: self)
    }
}

Example

func testing() {
    let lessons: [Lesson] = [
        .init(lessonDate: Date().addDays(2)),
        .init(lessonDate: Date().addDays(3)),
        .init(lessonDate: Date().addMonths(1).addDays(1)),
        .init(lessonDate: Date().addMonths(1).addDays(2)),
        .init(lessonDate: Date().addMonths(1).addDays(3)),
        .init(lessonDate: Date().addMonths(3)),
    ]

    let groupedDict = Dictionary(grouping: lessons, by: { $0.lessonDate.month })

    groupedDict
        .sorted(by: { a, b in a.key < b.key })
        .forEach {
            print("----- \($0.key.prettyMonth) -----")
            $0.value.forEach {
                print("\($0.lessonDate.prettyDate)")
            }
        }
}

Produces

----- August 2018 -----
29. Aug 2018 at 19:38:13
30. Aug 2018 at 19:38:13
----- September 2018 -----
28. Sep 2018 at 19:38:13
29. Sep 2018 at 19:38:13
30. Sep 2018 at 19:38:13
----- November 2018 -----
27. Nov 2018 at 19:38:13

Upvotes: 2

vadian
vadian

Reputation: 285190

This is a starting point to group the array with Dictionary(grouping:by:) by month and year using DateComponents.

The dictionary keys are dates. You can sort them and convert them to string with a DateFormatter and format "MMMM yyyy"

let grouped = Dictionary(grouping: lessons) { lesson -> Date in
    let calendar = Calendar.current
    let components = calendar.dateComponents([.year, .month], from: lesson.lessonDate!)
    return calendar.date(from: components) ?? Date(timeIntervalSince1970: 0)
}

If a date can't ever be created from the components, 1970-1-1 is used instead


Side note: As you force unwrap lessonDate anyway why is it optional at all?

Upvotes: 1

Related Questions