Matt Connolly's Blog

my brain dumps here…

iOS Core Data: Group by and count results

I looked in the usual places and couldn’t find any decent examples of using core data to do a group by and count. So I’m making one here.

Let’s suppose you have a bunch of Records, and each Record has a Status attribute among others, and you want a break down of how many have each Status. The SQL would be:

SELECT `Status`, COUNT(*) FROM `Records` GROUP BY `Status`

How do we do this with Core Data?

start with a fetch request:

NSFetchRequest* fetch = [NSFetchRequest fetchRequestWithEntityName:@"Record"];

set up an attribute description and expression description for the two values we want in the results:

NSEntityDescription* entity = [NSEntityDescription entityForName:@"Record"
                                          inManagedObjectContext:myManagedObjectContext];
NSAttributeDescription* statusDesc = [entity.attributesByName objectForKey:@"status"];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"url"]; // Does not really matter
NSExpression *countExpression = [NSExpression expressionForFunction: @"count:"
                                                          arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"count"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];

tell the fetch request to only fetch these, and group by the status attribute description

[fetch setPropertiesToFetch:[NSArray arrayWithObjects:statusDesc, expressionDescription, nil]];
[fetch setPropertiesToGroupBy:[NSArray arrayWithObject:statusDesc]];
[fetch setResultType:NSDictionaryResultType];
NSError* error = nil;
NSArray *results = [myManagedObjectContext executeFetchRequest:fetch
                                                         error:&error];

And voila. The result is an array of dictionaries, each with “status” and “count” keys and corresponding values. Even though the group by guarantees the grouped values be unique, there could be more than one grouped column, so it does make sense.
If you think that’s a lot of code, it is. Let’s compare that with the Rails solution:

Record.count(:group => :status)

And that returns a dictionary (Hash), keyed by status, with the counts in the values

Advertisements

6 responses to “iOS Core Data: Group by and count results

  1. Joe Galind 21 September, 2012 at 18:38

    What about grouping by two expressions? I haven’t been able to find an example on that…

  2. Troy 6 August, 2013 at 15:56

    How do you get the objects in the groups back???

    • mattconnolly 8 August, 2013 at 06:32

      The `NSArray *results` object will be an array of dictionaries containing the status and counts. If you want the full records, you need to run another query (fetch request).

  3. jane 22 October, 2014 at 18:50

    That’s a very detailed step-by-step instruction. But the alternative solution in last line, what is the type of “Record”?

  4. jane 24 October, 2014 at 14:14

    Hi matt, one question, I put a NSRelationshipDescription in the propertiesToFetch array. The request result contains array of dictionary as you said, but the value inside dictionary is of “_NSCoreDataTaggedObjectID “, do you have idea how to get an object or value out of it?

  5. Pingback: Is it possible to use group_concat with coredata? | Zolinas Answers

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: