Calculating sum for decimal values via Core Data not working properly?
first time I post to this round, so please bear with me if I don't follow all the rules properly.
I am writing an app for the iPhone (OS 3.1) and am trying to write some code which lets me add decimals. I have a Core Data entity called SimpleItem with a amount attribute. Here is the test case I wrote:
// Create and configure objects
SimpleItem *si1 = [self createSimpleItem:@"si1"];
si1.amount = [NSDecimalNumber decimalNumberWithMantissa:1000 exponent:0 isNegative:NO];
SimpleItem *si2 = [self createSimpleItem:@"s12"];
si2.amount = [NSDecimalNumber decimalNumberWithMantissa:2000 exponent:0 isNegative:NO];
// Need to save prior to fetching results with NSDictionaryResultType (known limitation)
[self save];
// Describe fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDe开发者_开发知识库scription entityForName:@"SimpleItem" inManagedObjectContext:self.context];
[request setEntity:entityDescription];
[request setResultType:NSDictionaryResultType];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"amount"];
// For whatever reason, evaluating this expression here is absolutely not working. Probably decimals aren't handled properly.
NSExpression *sumAmountExpression = [NSExpression
expressionForFunction:@"max:"
arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:@"amount"];
[expressionDescription setExpression:sumAmountExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Fetch the amounts
NSError *error = nil;
NSArray *array = [self.context executeFetchRequest:request error:&error];
If I execute this code through otest and debug it, I get an exception when the fetch request is executed: "-[NSDecimalNumber count]: unrecognized selector sent to instance."
Just evaluating the keyPathExpression without the aggregate function works fine, though.
The reference documentation shows exactly the same example so I'm wondering what I am doing wrong. Or could this be just a bug?
All the best, Harald
Copying your given source to a new iPhone project and running that in the simulator worked fine here. I was compiling against the 3.1.2 SDK on Snow Leopard.
This is what I used for the data model:
model description http://homepage.mac.com/aclark78/.Public/Pictures/test%20model.png
There must be something else going on causing your issue. Can you describe your model or simplify it further?
I think I had this problem before. IIRC, the problem is that you've set your fetch result type to All the examples in the docs use the default NSDictionaryResultType
but you're receiving it in an NSArray
. Try switching the fetch result type to NSManagedObjectResultType
which does return an array. NSManagedObjectResultType
.
In any case, the error message clearly results from an NSDecimal object being sent an array's count
message. You could confirm this by trapping the result in an id
. eg:
id *genericObj = [self.context executeFetchRequest:request error:&error];
NSLog("returned object=%@",genericObj);
NSLog("returned object class=%@",[genericObj class]);
Thanks for letting me know. The thing is that I actually don't want to use managed objects. After all, I am interested in the amounts only and this is what I get with the dictionary.
Also, trying to investigate the issue as you described didn't work as executing the fetch request immediately fails and I don't get anything back.
Any other ideas? Is there any other possibility to get aggregate values? Any "best practices"? At the moment I do the following (continuation of the code above):
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init]; [expressionDescription setName:@"amount"]; [expressionDescription setExpression:keyPathExpression]; [expressionDescription setExpressionResultType:NSDecimalAttributeType]; [request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Fetch the amounts
NSError *error = nil;
NSArray *array = [self.context executeFetchRequest:request error:&error];
if (array == nil) {
STFail(@"Fetch request could not be completed: %@", [error localizedDescription]);
}
STAssertEquals([array count], (NSUInteger) 2, @"Number of fetched objects != 2");
// Manually calculate the sum
NSDecimalNumber *sum = nil;
for (NSDictionary * dictionary in array) {
NSDecimalNumber *amount = (NSDecimalNumber *) [dictionary valueForKey:@"amount"];
if (!sum) {
sum = amount;
} else {
sum = [sum decimalNumberByAdding:amount];
}
}
STAssertNotNil(sum, @"Sum is nil");
STAssertTrue([sum isEqualToNumber:[NSDecimalNumber decimalNumberWithMantissa:3000 exponent:0 isNegative:NO]],
@"Sum != 3000: %@", sum);
Regards, Harald
精彩评论