2016-02-16 2 views
15

Мне нужно подсчитать последнюю полосу дней, но не могу понять, как это сделать. Например, я получил основные данные, как это:Как рассчитать полосу дней в основных данных?

|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), но не может понять, как использовать его в основных данных ,

Я думаю о других данных, которые будут сохраняться и обновляться каждый раз при открытии приложения пользователем, но с той же проблемой возникает проблема, если пользователь не открыл приложение.

Выход: мне нужно основать только отсчитывает дней в подряд или дату, когда он разорвать, поэтому для

Для первой таблицы: полоса = 2 или breakDate = 2016-02-14 - я стараюсь это , но мое решение неправильно, потому что второй таблицы

для второй таблицы: штрихом = 3 или breakDate = 2016-02-13 - не могу понять, как получить отсутствующий Дата

Важное обновление: Там будет быть клоо ud синхронизировать данные, поэтому я не вижу никакого решения внутри приложения, действительно нужно найти отсутствующую дату или isPresented = 0 в coredata

p.s. Я использую быстрый, если вы можете помочь мне через быстрый, было бы здорово, но я также понимаю Obj-C. И извините за мой плохой английский

+0

Вы можете получить данные с помощью какого-либо селектора. Это может быть помощь, но не для вашего вопроса. – Lumialxk

+0

Пожалуйста, покажите нам свой ожидаемый результат – sagi

+0

@sagi add Результат – Seko

ответ

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, но вы можете добавить его к кнопке, если вы хотите.

+0

Спасибо, Д. Грег, проверит его как можно скорее. Не совсем понять, но понять основную идею. – Seko

+0

что за цикл в calculateStreak делать? – Seko

+0

особенно если есть несколько дат, как в примере – Seko

1

ВСЕГО SQL Solution

Пожалуйста, обратите внимание, что это предполагает, что «сегодня», считается, что днем ​​в полосе для расчета. 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

ПРИМЕЧАНИЯ О скрипках: Они меняют даты, чтобы быть относительно «Сегодня «так, чтобы он точно проверил полосу.

Вот как это работает:

  • РЕЗЮМЕ: Мы будем «заполнить» недостающие дни с нулями, а затем сделать простую проверку, чтобы увидеть, когда максимальная 0 дата. Но мы должны принять во внимание, если на сегодняшний день нет строк, и если нет строк с нулями.
  • max_zero_dt: содержит одну строку, содержащую последнюю явную дату 0 или самую раннюю дату минус 1 день. Это должно уменьшить количество строк для последующих запросов.
  • days_to_check: Это сокращенное количество строк для проверки на основе того, когда установлено последнее 0.
  • missing_days: Нам нужно заполнить недостающие дни, поэтому мы получаем список всех дней минус 1 день. Мы также добавляем сегодня, если для этого нет строк.
  • all_days_dups: Мы просто комбинируем days_to_check и missing_days.
  • all_days: Здесь мы получаем «max» isPresent для каждого дня, так что теперь у нас есть истинный список дней для поиска, зная, что не будет пробелов с 0 для isPresent.
  • Окончательный запрос: это простой расчет, обеспечивающий полосу и дату начала.

допущениях:

  • ИМЯ ТАБЛИЦЫ IS: check_tab
  • Текущая дата должна быть в таблице с 1. В противном случае полоса равна 0. запрос может быть изменен, если это не случай.
  • Если в течение одного дня есть 0 и 1 для isPresent, приоритет 1, а полоса может продолжаться до предыдущих дней.
  • Эти базовые данные используют SQLite, и вышеупомянутый SQL, который работает в SQLite, также будет работать с базой данных Core Data.
Смежные вопросы