small medium large xlarge

Back to: All Forums  Core Data
Generic-user-small
07 Feb 2011, 15:26
Greg Robertson (21 posts)

I am wondering how to filter on a entity attribute that is virtual?

ie. the sum of two real attibutes?

for Example if I create a Class like:

#import <CoreData/CoreData.h>

@class Meal;

@interface Menu :  NSManagedObject  
{
}

@property (nonatomic, retain) NSDate * menuDate;
@property (nonatomic, retain) NSString * note;
@property (nonatomic, retain) NSNumber * protein;
@property (nonatomic, retain) NSNumber * carbs;
@property (nonatomic, retain) NSNumber * fat;
@property (nonatomic, retain) NSNumber * calories;
@property (nonatomic, retain) NSSet* meals;

- (NSNumber *) calorieSum;
- (NSNumber *) fatSum;
- (NSNumber *) proteinSum;
- (NSNumber *) carbSum;

- (NSNumber *) fatCalories;
- (NSNumber *) proteinCalories;
- (NSNumber *) carbCalories;

@end


@interface Menu (CoreDataGeneratedAccessors)
- (void)addMealsObject:(Meal *)value;
- (void)removeMealsObject:(Meal *)value;
- (void)addMeals:(NSSet *)value;
- (void)removeMeals:(NSSet *)value;

@end

With an Implementation of:

#import "Menu.h"
#import "Meal.h"
#import "CommonDefines.h"

@implementation Menu 

@dynamic menuDate;
@dynamic note;
@dynamic protein;
@dynamic carbs;
@dynamic fat;
@dynamic calories;
@dynamic meals;

- (NSNumber *) calorieSum
{
	return [self valueForKeyPath:@"meals.@sum.calorieSum"];
}

- (NSNumber *) fatSum
{
	return [self valueForKeyPath:@"meals.@sum.fatSum"];
}

- (NSNumber *) proteinSum
{
	return [self valueForKeyPath:@"meals.@sum.proteinSum"];
}

- (NSNumber *) carbSum
{
	return [self valueForKeyPath:@"meals.@sum.carbSum"];
}

- (NSNumber *) fatCalories
{
	double cals = [[self calorieSum] doubleValue];
	if (cals == 0)
		return 0;
	else
	{
		double fatGrams = [[self fatSum] doubleValue];
		NSInteger fatCals = ((fatGrams * kCaloriesInFatGram)*100)/cals;
		return [NSNumber numberWithInteger:fatCals];
	}
}

- (NSNumber *) proteinCalories
{
	double cals = [[self calorieSum] doubleValue];
	if (cals == 0)
		return 0;
	else
	{
		double proteinGrams = [[self proteinSum] doubleValue];
		NSInteger proteinCals = ((proteinGrams * kCaloriesInProteinGram)*100)/cals;
		return [NSNumber numberWithInteger:proteinCals];
	}
}

- (NSNumber *) carbCalories
{
	double cals = [[self calorieSum] doubleValue];
	if (cals == 0)
		return 0;
	else
	{
		double carbGrams = [[self carbSum] doubleValue];
		NSInteger carbCals = ((carbGrams * kCaloriesInCarbGram)*100)/cals;
		return [NSNumber numberWithInteger:carbCals];
	}
}


@end

I get an error which I try and use a predicate like:

NSPredicate *nutrientSearch = [NSPredicate predicateWithFormat:@”menuDate = %@ AND menuDate <= %@ AND fatCalories <= %d AND carbCalories <= %d AND proteinCalories <= %d”, infoWeek.startOfWeek, endOfWeek, fatCalories, carbCalories, proteinCalories];

with the error:

*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘keypath fatCalories not found in entity <NSSQLEntity Menu id=7>’

How can I make a key that I can use with an NSPredicate but still have it as a calculated value?

Thanks

Greg

Avatarsmall_pragsmall
11 Dec 2011, 20:23
Marcus S. Zarra (284 posts)

Short answer is you can’t.

The predicate hits the database directly, not objects. Therefore none of your calculated values will exist.

However, you can do a two pass filter by grabbing all of the objects that fit your non-calculated values first and then once they objects are loaded into memory run a second predicate that filters on your calculated values.

The other option is to store the calculated value in the object. I generally prefer to store the calculated value as it performs better.

You must be logged in to comment