如何计算核心数据中的连续天数?

时间:2016-02-16 07:43:53

标签: ios sql objective-c swift core-data

我需要计算最后几天,但无法弄清楚如何做到这一点。 例如,我得到了这样的核心数据:

|id| isPresent(Bool)| date(NSDate)|
|==|================|=============|
| 1|               0| 2016-02-11  |
| 2|               1| 2016-02-11  |
| 3|               1| 2016-02-12  |
| 4|               0| 2016-02-14  |
| 5|               1| 2016-02-15  |
| 6|               1| 2016-02-16  |
| 7|               1| 2016-02-16  |

我尝试检查最后没有呈现(isPresent = 0)的日期直到今天并获得2016-02-14 - 所以我可以数天,这很容易。

但是,如果我将2016-02-14标记为isPresented = 1(如下表中所示),我将最后没有提交2016-02-11 - 但它不正确2016-02-13没有数据,因此,此日期的提示应为0,而条纹应从此日期算起

|id| isPresent(Bool)| date(NSDate)|
|==|================|=============|
| 1|               0| 2016-02-11  |
| 2|               1| 2016-02-11  |
| 3|               1| 2016-02-12  |
| 4|               1| 2016-02-14  |
| 5|               1| 2016-02-15  |
| 6|               1| 2016-02-16  |
| 7|               1| 2016-02-16  |

我为缺少日期(sql server displaying missing dates)搜索了不同的条纹或sql reuest算法,但无法弄清楚如何在核心数据中使用它。

我想到了另一个数据,每次用户打开应用程序时都会保持条纹和更新,但是如果用户没有打开应用程序会遇到同样的问题。

输出: 我需要在连胜或日期中找到天数,这是为了

对于第一个表:streak = 2或者breakDate = 2016-02-14 - 我试试这个,但我的解决方案错了,因为第二个表

对于第二张表:streak = 3或breakDate = 2016-02-13 - 无法弄清楚如何获取缺失日期

重要更新: 会有云同步数据,所以我在应用内部看不到任何解决方案,确实需要在coredata中找到缺失日期或isPresented = 0

p.s。我正在使用swift,如果你可以通过swift帮助我,那会很棒,但我也理解Obj-C。抱歉我的英语不好

2 个答案:

答案 0 :(得分:1)

根据你的问题,我猜你有一个带有NSDate对象的item实体。以下是您可以使用的一些代码。

let userDefaults = NSUserDefaults.standardUserDefaults()
var moc: NSManagedObjectContext!

var lastStreakEndDate: NSDate!
var streakTotal: Int!



override func viewDidLoad() {
    super.viewDidLoad()


    // checks for object if nil creates one (used for first run)
    if userDefaults.objectForKey("lastStreakEndDate") == nil {
        userDefaults.setObject(NSDate(), forKey: "lastStreakEndDate")
    }

    lastStreakEndDate = userDefaults.objectForKey("lastStreakEndDate") as! NSDate

    streakTotal = calculateStreak(lastStreakEndDate)
}


// fetches dates since last streak
func fetchLatestDates(moc: NSManagedObjectContext, lastDate: NSDate) -> [NSDate] {
    var dates = [NSDate]()

    let fetchRequest = NSFetchRequest(entityName: "YourEntity")
    let datePredicate = NSPredicate(format: "date < %@", lastDate)

    fetchRequest.predicate = datePredicate

    do {
        let result = try moc.executeFetchRequest(fetchRequest)
        let allDates = result as! [NSDate]
        if allDates.count > 0 {
            for date in allDates {
                dates.append(date)
            }
        }
    } catch {
        fatalError()
    }
    return dates
}


// set date time to the end of the day so the user has 24hrs to add to the streak
func changeDateTime(userDate: NSDate) -> NSDate {
    let dateComponents = NSDateComponents()
    let currentCalendar = NSCalendar.currentCalendar()
    let year = Int(currentCalendar.component(NSCalendarUnit.Year, fromDate: userDate))
    let month = Int(currentCalendar.component(NSCalendarUnit.Month, fromDate: userDate))
    let day = Int(currentCalendar.component(NSCalendarUnit.Day, fromDate: userDate))

    dateComponents.year = year
    dateComponents.month = month
    dateComponents.day = day
    dateComponents.hour = 23
    dateComponents.minute = 59
    dateComponents.second = 59

    guard let returnDate = currentCalendar.dateFromComponents(dateComponents) else {
        return userDate
    }
    return returnDate
}


// adds a day to the date
func addDay(today: NSDate) -> NSDate {
    let tomorrow = NSCalendar.currentCalendar().dateByAddingUnit(.Day, value: 1, toDate: today, options: NSCalendarOptions(rawValue: 0))

    return tomorrow!
}

// this method returns the total of the streak and sets the ending date of the last streak
func calculateStreak(lastDate: NSDate) -> Int {
    let dateList = fetchLatestDates(moc, lastDate: lastDate)
    let compareDate = changeDateTime(lastDate)
    var streakDateList = [NSDate]()
    var tomorrow = addDay(compareDate)

    for date in dateList {
        changeDateTime(date)
        if date == tomorrow {
           streakDateList.append(date)
        }
        tomorrow = addDay(tomorrow)
    }

    userDefaults.setObject(streakDateList.last, forKey: "lastStreakEndDate")
    return streakDateList.count
}

我将来电置于viewDidLoad,但如果您愿意,可以将其添加到按钮中。

答案 1 :(得分:1)

所有SQL解决方案

请注意,这假定&#34;今天&#34;算作计算连胜的一天。 SQL以天数和条纹开始之前的日期返回条纹。

with max_zero_dt (zero_dt) as
(
  select max(dt)
    from ( 
         select max(isPresent) as isPresent, dt from check_tab group by dt
         union select 0, min(dt) from check_tab
         )
   where isPresent = 0
),
days_to_check (isPresent, dt) as
(
  select isPresent, dt
    from check_tab ct
    join max_zero_dt on ( ct.dt >= zero_dt )
),
missing_days (isPresent, dt) as
(
  select 0, date(dt, '-1 day') from days_to_check
  UNION
  select 0, date('now')
),
all_days_dups (isPresent, dt) as
(
  select isPresent, dt from days_to_check
  union
  select isPresent, dt from missing_days
),
all_days (isPresent, dt) as
(
  select max(isPresent) as isPresent, dt
  from all_days_dups
  group by dt
)
select cast(min(julianday('now') - julianday(dt)) as int) as day_streak
     , max(dt) as dt
  from all_days
 where isPresent = 0

这是第一个场景的平方英尺:http://sqlfiddle.com/#!7/0f781/2

以下是第二种情况的平方英尺:http://sqlfiddle.com/#!7/155bb/2

关于FIDDLES的注意事项:他们将日期改为相对于&#34;今天&#34;这样它就能准确地测试条纹。

以下是它的工作原理:

  • 摘要:我们将填写&#34;填写&#34;缺少零日,然后进行简单检查以查看最长0日期。但是,我们必须考虑今天是否没有行,以及没有零行。
  • max_zero_dt:包含一行,其中包含最新的显式0日期,或最早的日期减去1天。这是为了减少以后查询的行数。
  • days_to_check:这是根据最新的0时间检查的减少的行数。
  • missing_days:我们需要填写缺失的日子,所以我们会得到所有日子减去1天的列表。我们今天也添加了以防万一没有行。
  • all_days_dups:我们只是将days_to_check和missing_days结合起来。
  • all_days:在这里,我们得到了&#39; max&#39; isPresent为每一天,所以现在我们有了真实的搜索日期列表,知道isPresent与0之间没有任何差距。
  • 最终查询:这是提供条纹和开始日期的简单计算。

假设:

  • TABLE NAME IS:check_tab
  • 当前日期必须在表格中为1.否则,条纹为0.如果不是这样,则可以修改查询。
  • 如果一天同时为isPresent同时为0和1,则1优先,连续可以继续前几天。
  • 核心数据使用SQLite,而在SQLite中运行的上述SQL也可以与Core Data的数据库一起使用。