Louie Bertoncin
Louie Bertoncin

Reputation: 4033

Core Data NSFetchRequest Sort by Category Method Return Value

How do I sort my fetched results by a value that is returned by a method in a category of the entity I'm fetching?

In my category, I sum up several values from the entity's to-many relationship, then divide by the number of objects in the relationship, effectively creating an average that I return in my category method as a float value.

Here is my code:

In the Category.h

- (float)smallPenaltyAvg;

In the Category.m

- (float)smallPenaltyAvg{
    float smallPenaltyAvg = 0;
    for (Match *mtch in self.matches) {
        smallPenaltyAvg += [mtch.penaltySmall floatValue];
    }

    if ([self.matches count] > 0) {
        smallPenaltyAvg = (float)smallPenaltyAvg/(float)[self.matches count];
    }

    return smallPenaltyAvg;
}

And when I call it in the Core Data Table View Controller class that I created...

NSFetchRequest *poolRequest = [[NSFetchRequest alloc] initWithEntityName:@"Team"];
poolRequest.predicate = [NSPredicate predicateWithFormat:@"regionalIn.name = %@", _regionalToDisplay];
poolRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"smallPenaltyAvg" ascending:YES]];

And I have the Category.h file imported on every file previously mentioned outside of the Category.h file itself.

It gives me the error of:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath smallPenaltyAvg not found in entity <NSSQLEntity Team id=5>

Am I not allowed to do this?

If I am, what am I doing wrong?

Upvotes: 0

Views: 267

Answers (3)

Mundi
Mundi

Reputation: 80265

I do not think this has anything to do with the kind of persistent store.

The trick is to create an appropriate attribute in the managed object model, and mark it as Transient. Then override the getter of this attribute to do your calculations.

Now your fetch request should work as expected (although there are some caveats with fetched results controllers).

As for the SQLite problem, when you add the SQLite store with

 - (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType 
    configuration:(NSString *)configuration 
    URL:(NSURL *)storeURL 
    options:(NSDictionary *)options 
    error:(NSError **)error

just pass NSSQLiteStoreType as the storeType. The other options are binary and in-memory, so in this sense this is indeed the "default".

Upvotes: 1

Louie Bertoncin
Louie Bertoncin

Reputation: 4033

What I did to solve my problem was create a new attribute for every average or sum that I needed in the Team object from all of its Match objects' attributes and then created a method in the TeamCategory file that populated those averages and that method was called every time a Match object was inserted into the Team object. It took a while to do, but it works now. If there is a better solution, I'm still open for suggestions.

Upvotes: 0

Dan Shelly
Dan Shelly

Reputation: 6011

This is not possible when using a backing SQLite store.

My suggestion is you persist the average property, and maintain it yourself by overriding the Match setCategory: property and making the calculation there for every match added.

Upvotes: 0

Related Questions