I have an app whose purpose is to compare chronologically ordered time intervals, which are stored by Core Data (via MagicalRecord) as attributes of type double, on an entity called TimedActivity
. Ordering directions are supplied by attributes called activityOfInterest
and benchmarkActivity
on another entity named SearchSpecs
. The scheme may seem a bit overcomplicated since I'm pretty green, but that part of it works.
The problem is that getting percentages from two doubles appears to be a bit of a runaround, at least according to the research I've done. I don't need extreme precision. Round seconds are fine. I found a suggestion relating to the use of NSDecimalNumber
, but it too seemed like a long way around the corner.
Here is the relevant code in it's current state, with pseudocode to indicate my problem area:
#pragma mark - Go button case handlers
-(void) handleAvsAAction
{
// NSArray *searchSpecsObjects = [SearchSpecs MR_findAll];
// NSLog(@"SearchSpecs number of objects is %i",[searchSpecsObjects count]);
NSArray *searchSpecsArray = [SearchSpecs MR_findAll];
NSLog(@"searchSpecsArray count is %i", [searchSpecsArray count]);
SearchSpecs *thisSpec = [searchSpecsArray objectAtIndex:0];
NSLog(@"SearchSpec activityOfInterest should be %@", thisSpec.activityOfInterest);
NSLog(@"SearchSpec benchmarkActivity should be %@", thisSpec.benchmarkActivity);
// NSArray *activityOfInterestArray;
// NSArray *benchmarkActivityArray;
NSNumber *activityOfInterestDurationTotal;
NSNumber *benchmarkActivityDurationTotal;
NSPredicate *activityOfInterestPredicate = [NSPredicate predicateWithFormat:@"name == '%@'",thisSpec.activityOfInterest];
NSPredicate *benchmarkActivityPredicate = [NSPredicate predicateWithFormat:@"name == '%@'", thisSpec.benchmarkActivity];
activityOfInterestDurationTotal = [TimedActivity MR_aggregateOperation:@"sum:" onAttribute:@"duration" withPredicate:activityOfInterestPredicate];
NSLog(@"The sum of all the durations for the activity of interest is %zd", activityOfInterestDurationTotal);
benchmarkActivityDurationTotal = [TimedActivity MR_aggregateOperation:@"sum:" onAttribute:@"duration" withPredicate:benchmarkActivityPredicate];
NSLog(@"The sum of all the durations for the benchmark activity is %zd", benchmarkActivityDurationTotal);
[self doTheMathAvsA];
}
-(void) doTheMathAvsA
{
// Get the total and respective percentages of the totalled durations from the criteria distilled in handleAvsAAction
NSNumber *total;
total = (activityOfInterestDurationTotal + benchmarkActivityDurationTotal);
}
Edit: modified doTheMathAvsA
to clarify the desired result.
All help or suggestions appreciated!
Second edit:
OK, your answer below makes sense, and thanks @Martin R!
However, the two quantities in question here originate as NSTimeInterval
s, and as mentioned above, are stored as attributes of type double
, on an entity called TimedActivity
.
So, it seemed rational to me to slightly rewrite the code to extract them from the persistent store as NSTimeInterval
s, which I am assured are really just doubles. However, when I do this, I get this error:
Assigning to 'NSTimeInterval' (aka 'double') from incompatible type 'NSNumber *'
Here are the modified declarations:
NSTimeInterval activityOfInterestDurationTotal;
NSTimeInterval benchmarkActivityDurationTotal;
And here's where the error appears:
activityOfInterestDurationTotal = [TimedActivity MR_aggregateOperation:@"sum:" onAttribute:@"duration" withPredicate:activityOfInterestPredicate];
NSLog(@"The sum of all the durations for the activity of interest is %@", activityOfInterestDurationTotal);
benchmarkActivityDurationTotal = [TimedActivity MR_aggregateOperation:@"sum:" onAttribute:@"duration" withPredicate:benchmarkActivityPredicate];
NSLog(@"The sum of all the durations for the benchmark activity is %@", benchmarkActivityDurationTotal);
OK, I assume that the NSNumber
referred to in the error message is this property in the TimedActivity
managed object subclass, auto-generated from the data model:
@property (nonatomic, retain) NSNumber * duration;
So my question becomes:
Is it really necessary to resort to such seemingly ever-widening circles of conversion and retro-conversion to perform such a seemingly simple calculation? Or am I missing a more straightforward solution?
Thanks!
You cannot perform arithmetic directly on NSNumber
objects. The easiest solution is
to convert them to double
for the addition:
double tmp = [activityOfInterestDurationTotal doubleValue] + [benchmarkActivityDurationTotal doubleValue];
and the result back to NSNumber
, if necessary:
NSNumber *total = @(tmp);
Update: By default, the Xcode generated accessor methods use Objective-C objects even for primitive Core Data types such as "Double". You can change that by selecting the "Use scalar properties for primitive data types" option when creating the subclass files. Then a "Double" property is declared as
@property (nonatomic) double activityOfInterestDurationTotal;
and you can access it "directly" as for example
NSTimeInterval duration = thisActivity.duration;
because NSTimeInterval
is just another name for double
.
But there is another problem: The MagicalRecord "convenience method" MR_aggregateOperation:
uses a special fetch request with NSDictionaryResultType
to
fetch the sum of all duration
values. And even if you chose to use a scalar property
for the duration, the result of MR_aggregateOperation:
is always some Objective-C object, in this case NSNumber
, there is no way around it.
So the only way to avoid a conversion between NSNumber
and double
would be to use a scalar
property as described above, and instead of using MR_aggregateOperation:
, fetch all
objects and add the duration
values yourself in a simple loop.
But note that the fetch request with the aggregate operation performs the calculations on the SQLite level, this is probably more effective then actually fetching all objects.