简体   繁体   中英

Swift: How to create an array of date arrays [ [Date] ], after comparing/splitting two date arrays [Date]

Introduction

I'm creating a calendar app and I'm in need of managing of a date array in order to provide data for a UITableView .

Context

The data model is of type [Date : [CalendarEvent]] , where CalendarEvent is a NSManagedObject subclass that I sort into a dictionary grouped on the events associated date attribute. And I have one section for each key of that dictionary in my UITableView (I don't fetch all calendar events at once). However I would like to add more sections to display the date interval gaps between dates with events, using an array of type [[Date]]

  • Clarification: Picture that my calendar app has 2 events stored. One event 2018-12-05 and one event 2018-12-09, and todays date is 2018-12-01. In that case I would like to retrieve an array like the following: [ [2018-12-01, 2018-12-02, 2018-12-03, 2018-12-04], [2018-12-05], [2018-12-06, 2018-12-07, 2018-12-08], [2018-12-09], [2018-12-10] ] where each of those dates are of type Date of course. (Which would yield in 4 sections)

Question

  • How do I sort/split my arrays to satisfy the format [[Date]] (explained in the "clarification" above)?
    • If there is a simpler way to achieve the same result, that would also be regarded as an answer.

My attempt

I've scaled down to only display the necessary parts.

class CalendarViewController: UIViewController {
    private let currentCalenar = Calendar.current
    var events : [Date: [CalendarEvent]]? // Events is fetched from todays date and 10 days forward.
    var sectionDates : [[Date]]?

    func getDatesWithEvents() -> [Date]? {
        if let keyArray = events?.keys {
            var dateArray = Array(keyArray)
            dateArray.sort { $0 < $1 }
            return dateArray
        }
        return nil
    }

    func getSectionDatesArray() -> [[Date]]? { 
        var sectionDatesArray : [[Date]] = []
        var currentDate = currentCalendar.startOfDay(for: Date())
        guard let endDate = currentCalendar.date(byAdding: .day, value: 9, to: currentDate), let datesWithEvent = getDatesWithEvents() else { return nil }
        while currentDate < endDate {
            if datesWithEvent.contains(currentDate) {
                sectionDatesArray.append([currentDate])
                sectionDatesArray.append([])
            } else {
                if !sectionDatesArray.isEmpty {
                    sectionDatesArray[sectionDatesArray.count - 1].append(currentDate)
                } else {
                    sectionDatesArray.append([currentDate])
                }
            }
            currentDate = currentCalendar.date(byAdding: .day, value: 1, to: currentDate)!
        }
        sectionDatesArray.removeAll { (sequence) -> Bool in
            sequence.isEmpty
        }
        return sectionDatesArray
   }

Thanks for reading my question.

In general, using multiple data sources on a single UITableView brings bad luck. .. you might consider changing your data model.

In your example I would consider merging those two data sources in a way that values of dates with no events will be nil , so you would declare it as var dates: [Date: [CalendarEvent]?]? .

Another way- you would cut off this dictionary and use an array to store objects of type (suggested name) CalendarItem - in there you may store the item's Date and [CalendarEvent]?

Nevertheless there are many ways to do that and I'm not going to cover them all...

If you want to stick with your model, here's a function I've made. It's probably not the optimal solution and there's some magic that can do it in 4 lines :) but here you go-

var currentDate = Date()

// just made up dates
let datesWithEvents: [Date] = [
    Calendar.current.date(byAdding: .day, value: 5, to: currentDate)!,
    Calendar.current.date(byAdding: .day, value: 10, to: currentDate)!
]

let endDate = Calendar.current.date(byAdding: .day, value: 15, to: currentDate)!

var dates: [[Date]] = []
while currentDate < endDate{

    if datesWithEvents.contains(currentDate){
        dates.append([currentDate])
        dates.append([])
    }else{

        if !dates.isEmpty{
            dates[dates.count - 1].append(currentDate)
        }else{
            dates.append([currentDate])
        }
    }

    currentDate = Calendar.current.date(byAdding: .day, value: 1, to: currentDate)!
}

/* prints
 [
    [
        2018-12-04 17:57:44 +0000
        2018-12-05 17:57:44 +0000
        2018-12-06 17:57:44 +0000
        2018-12-07 17:57:44 +0000
        2018-12-08 17:57:44 +0000
    ],
        [2018-12-09 17:57:44 +0000],
    [
        2018-12-10 17:57:44 +0000,
        2018-12-11 17:57:44 +0000,
        2018-12-12 17:57:44 +0000,
        2018-12-13 17:57:44 +0000
    ],
        [2018-12-14 17:57:44 +0000],
    [
        2018-12-15 17:57:44 +0000,
        2018-12-16 17:57:44 +0000,
        2018-12-17 17:57:44 +0000,
        2018-12-18 17:57:44 +0000
    ]
 ]
 */

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