如何计算核心数据的连续天数?
Posted
技术标签:
【中文标题】如何计算核心数据的连续天数?【英文标题】:How to calculate streak of days in core data? 【发布时间】:2016-02-16 07:43:53 【问题描述】:我需要计算最后连续几天,但不知道该怎么做。 例如我得到这样的核心数据:
|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 的数据,所以这个日期的 isPresented 应该是 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 reuests (sql server displaying missing dates),但无法弄清楚如何在核心数据中使用它。
我正在考虑另一种数据,每次用户打开应用程序时都会保持连续和更新,但如果用户没有打开应用程序会遇到同样的问题。
输出: 我需要找到连续的天数或中断的日期,所以对于
对于第一个表:streak = 2 或 breakDate = 2016-02-14 - 我尝试这个,但我的解决方案错误,因为第二个表
对于第二张表:streak = 3 或 breakDate = 2016-02-13 - 无法弄清楚如何获取缺失日期
重要更新: 会有云同步数据,所以我在app里面看不到任何解决方案,真的需要在coredata中找到丢失的日期或isPresented = 0
p.s.如果你能通过 swift 帮助我,我正在使用 swift,它会很棒,但我也了解 Obj-C。对不起我的英语不好
【问题讨论】:
您可以使用一些选择器获取数据。这可能对您的问题有所帮助。 请告诉我们您的预期输出 您的主要问题是将缺少的日期(天?)用 isPresent 填充为 0。但这也提出了这个问题:您的意思是每天吗?或者可能只从周一到周五,等等?也许在应用程序启动时,它会检查当前日期,并从上次开始时填写缺失的日期?然后,您可以使用 fetchLimit 和 isPresent == 0 的条件和降序来查找您的日期。 @Lumialxk true 我使用谓词,为我的错误解决方案排序,fut 无法理解如何使用复杂的 sql 请求(如有问题的链接)来处理核心数据 @Larme 主要问题是获取 coredata ot date 中缺少的日期,其中 isPresent 为 0 我考虑设置缺少的日期,但认为方向错误,用户可能会忘记应用程序 100+ 或更多天并为所有天设置 0 不是一个好主意。顺便说一句,如果用户只是安装应用程序,我不知道该怎么办,检查共享变量是否为空,将其设置为今天,如果不为空,则将所有日期设置为今天 isPresent=0,但如果用户通过通知检查 isPresent?在通知操作中做同样的事情吗? 【参考方案1】:根据您的问题,我猜您有一个带有 NSDate 对象的项目实体。下面是一些你可以用来做的代码。
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
,但如果你愿意,可以将它添加到按钮中。
【讨论】:
谢谢,D. Greg,会尽快检查。不完全理解,但理解主要思想。 calculateStreak 中的 for 循环做什么? 特别是如果有多个日期,例如示例 它将每个日期设置为具有相同的小时、分钟和秒。然后将该日期与数组中的前一个日期进行比较。如果没有循环,它将以 24 小时的间隔准确判断日期。那将是一个问题。例如,连胜是计算每天的锻炼次数。如果用户在第一天晚上 10 点锻炼,然后在第二天早上 8 点锻炼,则将被视为在同一 24 小时内。不理想。因此,循环只计算天数(嗯,23 小时、59 分钟、59 秒天)。【参考方案2】:所有 SQL 解决方案
请注意,这假定“今天”计为连续计算中的一天。 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
这是第一个场景的 sqlfiddle:http://sqlfiddle.com/#!7/0f781/2
这是第二种情况的 sqlfiddle:http://sqlfiddle.com/#!7/155bb/2
关于 FIDDLES 的注意事项:他们将日期更改为相对于“今天”,以便准确测试连胜。
这是它的工作原理:
摘要:我们将用零“填充”缺失的日期,然后进行简单检查以查看最大 0 日期是什么时候。但是,我们必须考虑今天是否没有行,以及是否没有行为零。 max_zero_dt:包含单行,其中包含最新的显式 0 日期,或最早的日期减去 1 天。这是为了减少以后查询的行数。 days_to_check:这是根据最新的 0 的时间减少的要检查的行数。 missing_days:我们需要填写缺失的天数,所以我们得到一个所有天数减去 1 天的列表。我们今天还会添加,以防它没有行。 all_days_dups:我们简单地将 days_to_check 和 missing_days 结合起来。 all_days:这里我们得到了每一天的“最大”isPresent,所以现在我们有了要搜索的真实天数列表,知道 isPresent 为 0 不会有任何间隙。 最终查询:这是提供连续和开始日期的简单计算。假设:
表名是:check_tab 当前日期必须在表中带有 1。否则,连续为 0。如果不是这种情况,可以修改查询。 如果某一天的 isPresent 同时有 0 和 1,则 1 优先,并且连续性可以延续到前几天。 Core Data 使用 SQLite,上述 SQLite 中的 SQL 也适用于 Core Data 的数据库。【讨论】:
以上是关于如何计算核心数据的连续天数?的主要内容,如果未能解决你的问题,请参考以下文章