How to sort Core Data results based on an attribute of related object collection?
Asked Answered
T

2

6

Setup: I have a collection of parent objects, call them ObjectA. Each ObjectA has a one-to-many relation to ObjectB. So, one ObjectA may contain 0..n ObjectB-s, and each ObjectB has a specific ObjectA as its parent.

Now, I would like to do a Core Data fetch of ObjectA-s, where they are sorted by their latest ObjectB. Is it possible to create a sort descriptor for that?

There is a related question that describes exactly the same situation. The answer suggests denormalizing the attribute from ObjectB into ObjectA. This would be OK if there really is no way to do this with one fetch request.

The related question also mentions:

Actually, I just had an idea! Maybe I can sort Conversations by [email protected]

I tried. It doesn’t seem to be possible. I get this error:

2012-10-05 17:51:42.813 xxx[6398:c07] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: 'Keypath containing
KVC aggregate where there shouldn't be one; failed to handle
[email protected]'

Is denormalizing the attribute into ObjectA the only/best solution?

Tillo answered 5/10, 2012 at 14:55 Comment(0)
T
0

You could add an attribute in ObjectB which is the time stamp of the add date, then in the fetch request you can do something like this:

NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"objectB.addTime" ascending:YES];
...
fetchRequest.sortDescriptors = @[descriptor];
Trehalose answered 1/5, 2013 at 17:35 Comment(1)
This didn't work for me: 'NSInvalidArgumentException', reason: 'to-many key not allowed here'Krieger
B
0

I know this question is a bit old but what I did was get all ObjectBs, iterate over the results and pull out the ObjectB property and add it to a new array.

NSFetchRequest *fetchRequest = [NSFetchRequest new];
[fetchRequest setEntity:self.entityDescForObjectB];

// sort
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
[fetchRequest setSortDescriptors:@[sortDescriptor]];

NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
    NSLog(@"Error fetching objects: %@", error.localizedDescription);
    return;
}

// pull out all the ObjectA objects
NSMutableArray *tmp = [@[] mutableCopy];
for (ObjectB *obj in fetchedObjects) {
    if ([tmp containsObject:obj.objectA]) {
        continue;
    }
    [tmp addObject:obj.objectA];
}

This works because CoreData is an object graph so you can work backwards. The loop at the end basically checks to see if the tmp array already has a specific ObjectA instance and if not adds it to the array.

It's important that you sort the ObjectBs otherwise this exercise is pointless.

Bughouse answered 3/3, 2015 at 14:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.