Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Group by day in NSDate with NSPredicate - needed to create multiple UITableView sections

See also

Core Data Sort By Date With FetchedResultsController into sections

Is there a way to group objects from a CoreData entity by date and use them to create multiple sections on a UITableView control?

Essentially, I need something equivalent to the following pseudo-SQL, where my_group_criteria() groups all tests by day:

SELECT * FROM tests GROUP BY my_group_criteria(date)

As an example, the query would group the following rows into 3 separate sections:

[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]

As already answered in 2, NSPredicate is not for grouping entities so this may not be the way to go.

Given this, how do I go about creating multiple sections in a UITableView based on some criteria similar to what SQL GROUP BY would do?

I would like to avoid accessing the SQL-lite database directly if at all possible.

Related question 1: NSPredicate: filtering objects by day of NSDate property

Related question 2: NSPredicate something equivalent of SQL's GROUP BY

like image 772
bizz84 Avatar asked Feb 18 '13 18:02

bizz84


1 Answers

You can modify the DateSectionTitles sample project from the Apple Developer Library according to you needs.

First, you have to modify the accessor function for the transient sectionIdentifier property to create a section identifier based on year+month+day (instead of year+month only):

- (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;
}

Second, the titleForHeaderInSection delegate method must be changed, but the idea is the same: extract year, month and day from the section identifer, and create a header title string from that:

- (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;
}
like image 157
Martin R Avatar answered Oct 17 '22 05:10

Martin R