Как группировать по дням в NSDate с помощью NSPredicate - необходимо создать несколько разделов UITableView - программирование
Подтвердить что ты не робот

Как группировать по дням в NSDate с помощью NSPredicate - необходимо создать несколько разделов UITableView

См. также

Основные данные сортируются по дате с помощью FetchedResultsController в разделы

Есть ли способ группировать объекты из объекта CoreData по дате и использовать их для создания нескольких разделов в элементе управления UITableView?

По сути, мне нужно что-то эквивалентное следующему псевдо-SQL, где my_group_criteria() группирует все тесты по дням:

SELECT * FROM tests GROUP BY my_group_criteria(date)

В качестве примера, запрос сгруппировал следующие строки в 3 отдельных раздела:

[test1 | 2013-03-18 15.30.22]
[test2 | 2013-03-18 14.30.22]
[test3 | 2013-03-18 13.30.22]

[test4 | 2013-03-17 18.30.22]
[test5 | 2013-03-17 19.30.22]

[test6 | 2013-03-15 20.30.22]

Как уже было сказано в 2, NSPredicate не предназначен для группировки объектов, поэтому это может быть не так.

Учитывая это, как мне заняться созданием нескольких разделов в UITableView на основе некоторых критериев, аналогичных тем, что будет делать SQL GROUP BY?

Я хотел бы избежать доступа к базе данных SQL-lite напрямую, если это вообще возможно.

Связанный вопрос 1:   NSPredicate: фильтрация объектов по дням свойства NSDate

Связанный вопрос 2: NSPredicate что-то эквивалентное SQL GROUP BY

4b9b3361

Ответ 1

Вы можете изменить DateSectionTitles образец проекта из библиотеки разработчиков Apple в соответствии с вашими потребностями.

Во-первых, вы должны изменить функцию доступа для транзиторной sectionIdentifier свойство создать идентификатор раздела, основанный на год + месяц + день (вместо года + месяц только):

- (NSString *)sectionIdentifier {

    // Create and cache the section identifier on demand.

    [self willAccessValueForKey:@"sectionIdentifier"];
    NSString *tmp = [self primitiveSectionIdentifier];
    [self didAccessValueForKey:@"sectionIdentifier"];

    if (!tmp) {
        /*
         Sections are organized by month and year. Create the section identifier
         as a string representing the number (year * 10000 + month * 100 + day);
         this way they will be correctly ordered chronologically regardless of
         the actual name of the month.
         */
        NSCalendar *calendar = [NSCalendar currentCalendar];

        NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
                               fromDate:[self timeStamp]];
        tmp = [NSString stringWithFormat:@"%d", [components year] * 10000 + [components month] * 100  + [components day]];
        [self setPrimitiveSectionIdentifier:tmp];
    }
    return tmp;
}

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

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

    id <NSFetchedResultsSectionInfo> theSection = [[fetchedResultsController sections] objectAtIndex:section];

    NSInteger numericSection = [[theSection name] integerValue];
    NSInteger year = numericSection / 10000;
    NSInteger month = (numericSection / 100) % 100;
    NSInteger day = numericSection % 100;

    // Create header title YYYY-MM-DD (as an example):
    NSString *titleString = [NSString stringWithFormat:@"%d-%d-%d", year, month, day];
    return titleString;
}

Ответ 2

Предполагая, что идея состоит в том, чтобы сгруппировать массив объектов в массив массивов объектов, подход, который я бы принял, заключается в этом, начиная с поиска возможных значений даты (или того, что вы хотите группировать), а затем фактически сортировки это.

NSArray *values = ...;
NSArray *sorting = [values valueForKey:@"@distinctUnionOfObject.propertyToGroupBy"];
NSUInteger sectionCount = [sorting count];
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
for (int i = 0; i < sectionCount; i++)
    [sections addObject:[NSMutableArray array]];

[values enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // Figure out the section for this object
    NSUInteger section = [sorting indexOfObject:obj.propertyToGroupBy];
    [[sections objectAtIndex:section] addObject:obj];
}];

// sections will contain as many sections as there were unique values
// each of those values is an array containing values that matched the criteria

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

EDIT: после повторного рассмотрения вопроса свойство propertyToGroupBy должно будет нормализовать значение, то есть удалить временные компоненты даты, поскольку группировка выполняется с помощью уникальных значений (т.е. сравнение основано on isEqual:)