views:

53

answers:

1

I have a view where I would like to get an average value of a "percentage" property without loading all the objects (and their other properties) into memory from Core Data. I have found out how to do this in Apple's docs, but the problem is I would like to limit the objects whose percentages are being averaged to ones that have another property called "numberAsked" greater than 0. I thought this was something I could do with an NSPredicate.

Here is what I have:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Statistics" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];

NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"numberAsked > 0"];
[request setPredicate:searchPredicate];

[request setResultType:NSDictionaryResultType];


NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"percentCorrect"];
NSExpression *averagePercentExpression = [NSExpression expressionForFunction:@"average:"
                                                                   arguments:[NSArray arrayWithObject:keyPathExpression]];

NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:@"averagePercentageCorrect"];
[expressionDescription setExpression:averagePercentExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];

[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];

NSError *error;
NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];

What happens is that it seems to fetch all objects and ignore the NSPredicate. I don't know if changing the request's result type does something to this, or I need to filter the objects with another NSExpression, or what.

My testing values are that right now all objects have the numberAsked property set to 0, so theoretically my final objects array should have a count of 0, from where I would specify "N/A" for the percentage. But that is not happening.

I appreciate any help!

A: 

You can't pass an expression to setPropertiesToFetch:. It expects an array of NSPropertyDescription objects. Im kind of surprised it didn't crash.

You want something like this:

NSManagedObject *mo;
for (int i=0; i<5; i++) {
    mo=[NSEntityDescription insertNewObjectForEntityForName:@"Test" inManagedObjectContext:self.moc];
    [mo setValue:[NSNumber numberWithInt:i] forKey:@"numAttrib" ];      
}
[self saveContext];
NSFetchRequest *fetch=[[NSFetchRequest alloc] init];
NSEntityDescription *testEntity=[NSEntityDescription entityForName:@"Test" inManagedObjectContext:self.moc];
[fetch setEntity:testEntity]; 
NSDictionary *propDict=[testEntity propertiesByName];
[fetch setPropertiesToFetch:[NSArray arrayWithObject:[propDict valueForKey:@"numAttrib"]]];
NSArray *fetchReturn=[self performFetch:fetch];//<-- my custom boilerplate
NSLog(@"fetchReturn=%@",fetchReturn);
id theAvg=[fetchReturn valueForKeyPath:@"@avg.numAttrib"];
NSLog(@"theAvg=%f",[theAvg floatValue]);

... which prints:

fetchReturn=(
    "<NSManagedObject: 0x5d2fae0> (entity: Test; id: 0x5d0c310 <x-coredata://384EFAF2-1921-4CB0-85B2-402DBA145615/Test/p1> ; data: {\n    numAttrib = 3;\n})",
    "<NSManagedObject: 0x5d35df0> (entity: Test; id: 0x5d34480 <x-coredata://384EFAF2-1921-4CB0-85B2-402DBA145615/Test/p2> ; data: {\n    numAttrib = 1;\n})",
    "<NSManagedObject: 0x5d33890> (entity: Test; id: 0x5d31810 <x-coredata://384EFAF2-1921-4CB0-85B2-402DBA145615/Test/p3> ; data: {\n    numAttrib = 2;\n})",
    "<NSManagedObject: 0x5d36d10> (entity: Test; id: 0x5d0dfa0 <x-coredata://384EFAF2-1921-4CB0-85B2-402DBA145615/Test/p4> ; data: {\n    numAttrib = 4;\n})",
    "<NSManagedObject: 0x5d38f00> (entity: Test; id: 0x5d302a0 <x-coredata://384EFAF2-1921-4CB0-85B2-402DBA145615/Test/p5> ; data: {\n    numAttrib = 0;\n})"
)
theAvg=2.000000

If you want to go even lighter weight. You can add:

[fetch setResultType:NSDictionaryResultType];

... which returns an array of single key dictionaries which like so:

fetchReturn=(
        {
        numAttrib = 2;
    },
        {
        numAttrib = 0;
    },
        {
        numAttrib = 4;
    },
        {
        numAttrib = 1;
    },
        {
        numAttrib = 3;
    }
)
float=2.000000
TechZen
I'm not sure if this is what I want, or if I was clear enough...My code is similar to the Apple code below. I pass an array of NSExpressionDescriptions (not NSExpressions) to setPropertiesToFetch:. The code is under "Fetching Specific Values":http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html#//apple_ref/doc/uid/TP40002484-SW6I get the average of the percent property just fine, it's just that I don't necessarily want to average ALL the percents - just the percents of the objects where another property of that object (numAsked) is > 0.