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 NSTimeIntervals, 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 NSTimeIntervals, 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.
Related
I don't very understand about the diference between (NSInteger)aNumberValue and [aNumberValue integerValue], then do some test. For example, here is a response data from server:
You can see it's an int but the value is hold by NSNumber. I retrieve the data by writting NSInteger count = (NSInteger) dic[#"count"];, and in Xcode debug area, saw this:
it's really a strange value but when I run po count and saw this:
anyway, the value is correct, but another strange thing is:
the number 2 is not less than 100!
Then I try NSInteger a = [dic[#"count"] integerValue] and saw the normal value in Xcode debug area:
and:
So, I am a little bit confused, what's the deference between (NSInteger)aNumberValue and [aNumberValue integerValue]?
NSNumber is a class; NSInteger is just a typedef of long, which is a primitive type.
dic[#"count"] is a pointer, which means that dic[#"count"] holds an address that points to the NSNumber instance. NSNumber has a method called integerValue which returns an NSInteger as the underlying value that the NSNumber instance represents. So you can conclude that [dic[#"count"] integerValue] gets you a long, and that's how you retrieve the value out of NSNumber.
You don't retrieve the value of NSNumber by casting it to NSInteger. That's because dic[#"count"], as I said, is a pointer. So by writing
NSInteger count = (NSInteger) dic[#"count"];
you are actually casting the pointer itself to an NSInteger, which has nothing to do with the actual represented value. The value 402008592 you see is just a decimal representation of the value of the pointer, which is an address.
The command po is used for printing objects, so lldb will actually try to print out the object at the address of count. That's why you get 2 back using po. You can try p count and you'll get 402008592.
About po count < 100: The expression (count < 100) is evaluated first; since count is really just an NSInteger of 402008592, it will evaluate to false.
The root issue is that Objective-C collection classes can only store Objective-C objects, not primitive types like float, int or NSInteger.
Therefore NSNumber provides a mechanism to store numbers and booleans in object form.
The value 402008592 looks like an address, so it's probably an NSNumber object containing an NSInteger value.
Don't get confused by the NS prefix of NSInteger and NSUInteger; they are still primitive types and not objects like NSNumber.
I have two very large NSMutableArray of strings containing more than 40k records each. I have to take each element from one array and sort that string into another array then make a new array which conatins only those records that are in both array. I have implemented the following code which take too much time as well as a lot of memory space also (crash in device). Are there any ways to solve this problem in a more efficient manner.
// _perArray and listArray contains more then 30K records each
for(NSString *gak in _perArray){
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF LIKE[c] %#",gak];
NSArray *results = [listArray filteredArrayUsingPredicate:predicate];
if(results.count>0){
[_resultArray addObject:results[0]];
}
}
Use binary search
index sort one array (the one with less records)
this will enable the usage of binary searching
sort the lesser array just to need less memory for index array
loop through the second array
for each record binary search in the first array
if found add record to output array
do not forget to preallocate the output array to avoid reallocation slowdowns
What it means:
Let N,M be the array sizes where N<=M
naive approach is O(N.M)
this approach (depending on used sort) leads to O(N.log(N).log(M))
Sort both arrays and use single pass incremental search
the complexity will lead to something like O((N.log(N))+(M.log(M))+M)
which in therms of complexity turns to O(M.log(M))
So:
index sort booth arrays
loop through M
increment index for array with lesser record
if match found add it to output array
To be more specific bullet 2 will be something like this (if arrays are sorted ascending):
// variables
string m[M],n[N],o[N]; // your arrays any string type with overloaded <,== operators
int M,N,O; // arrays sizes
int ixm[M],ixn[N]; // indexes for index sort
int i,j;
// bullet 2
for (i=0,j=0,O=0;;)
{
if (m[ixm[i]]==n[ixn[j]]) { o[O]=m[ixm[i]]; O++; }
if (m[ixm[i]]< n[ixn[j]]) { if (i<M) i++; else { if (j<N) j++; else break; }}
else { if (j<N) j++; else { if (i<M) i++; else break; }}
}
If you encode the string comparisons right you can do booth if conditions with single comparison
[notes]
if you do not want to use any of these approaches then there is also another way
you can add flag to one array telling you if it is already used
if it is skip the use of it during your comparisons
that will speed up your naive approach about 2 times
from M.N string comparisons you will need to do just M.N/2
If you have too big data chunks to fit in memory
then segmentate both arrays to some size fit to memory/Cache/...
and first index sort all segments
then do one of the above approach on all segments combinations
the only thing you need to add is checking if O[] does not already contain added string
if you arrays does not have multiples of the same string then this is not the case
otherwise keep O[] sorted or index sorted
and check by binary search ...
this segmentation will be speeded up with the used flag significantly
Always balance the performance issues with the frequency this code path is called. Going the database route might introduce a whole new set of issues to deal with while simply performing the sort in the background, and cutting down the size of the array first might be good enough.
Remove the duplicates first using NSMutableSet
Add all the objects, NSString in this case, to an NSMutableSet. This will eliminate the duplicates. Then sort the remaining objects.
NSArray *array1;
NSArray *array2;
NSMutableSet *mutableSet = [[NSMutableSet alloc] initWithArray:array1];
[mutableSet addObjectsFromArray:array2];
NSSortDescriptor *sortDescriptor = nil; // You'll need to create a sort descriptor.
NSArray *result = [mutableSet sortedArrayUsingDescriptors:#[sortDescriptor]];
// Alternative
NSArray *result = [[mutableSet allObjects] sortedArrayUsingSelector:#selector(compare:)];
I wrote a quick Obj-C test you can try at the command-line.
Run it in the background and return when finished.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
// Perform the sorting
dispatch_async(dispatch_get_main_queue(), ^(void) {
// Tell the main thread I'm done.
});
});
Rather than a long if statement, what is a more compact and readable way to verify if a string is contained in a collection of possible values? In other words, check if a value is within a domain?
I want to do something like this…
NSArray* domain = [NSArray arrayWithObjects:#"dog", #"cat", #"bird", nil];
BOOL valueFoundInDomain = [domain containsObject:#"elephant"];
But I'm concerned about equality checking with NSString. I want to check the value of the text, not object identity.
The documentation for NSArray says the containsObject method uses the isEqual method. But I cannot find in the documentation for NSString an explanation for its implementation of isEqual. The presence of the isEqualToString method suggests that isEqual may be doing something else. If that something else involves interning of string objects, then experimenting myself may give misleading results, so I'd like a documented answer.
I never use -isEqualToString:, only -isEqual: and it just works as it should! (I do this for years.)
NSString is implementing -isEqual: and it returns YES if the other object is a string and it has the same contents.
In Apples Objective-C documentation, methods that are overridden from a baseclass are often not explicitely documented. But -isEqual: is one of the few methods that is implemented in all foundation classes where it makes sense.
The isEqual method does an additional type check to ensure you are comparing two objects of the same class.
IsEqualToString assumes you are sending a string and will crash if you send a nil or object of another type.
Your code looks good for its use case.
Lets Try Using This
NSArray* domain = [NSArray arrayWithObjects:#"dog", #"cat", #"bird", nil];
NSIndexSet *indexes = [domain indexesOfObjectsWithOptions:NSEnumerationConcurrent passingTest:^BOOL(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return [obj isEqualToString:#"elephant"];
}];
// Where indexes contains matched indexes of array elements
Here isqualToString: Returns a Boolean value that indicates whether a given string is equal to the receiver using a literal Unicode-based comparison.isEquealTo: Returns a Boolean value that indicates whether the receiver and a given object are equal. When you know both objects are strings, isEqualToString: is a faster way to check equality than isEqual:
I have the need to obtain the maximum value of a property of a collection of custom objects of the same class. The objects are stored in a NSArray, and the property happens to be another NSArray of numbers.
Let me explain in detail:
NSArray *samples; // of CMData, 4000 elements
CMData is a class that models a sample, for a specific moment in time, of a set of different channels that can have different values.
#interface CMData : NSObject
#property (nonatomic) NSUInteger timeStamp;
#property (nonatomic, strong) NSArray *analogChannelData; // of NSNumber, 128 elements
#end
(I have stripped other properties of the class not relevant to the question)
So for example, sample[1970] could be:
sample.timeStamp = 970800
sample.analogChannelData = <NSArray>
[
[0] = #(153.27)
[1] = #(345.35)
[2] = #(701.02)
...
[127] = #(-234.45)
]
Where each element [i] in the analogChannelData represents the value of that specific channel i for the timeStamp 970800
Now I want to obtain the maximum value for all the 4000 samples for channel 31. I use the following code:
NSUInteger channelIndex = 31;
NSMutableArray *values = [[NSMutableArray alloc] init]; // of NSNumber
// iterate the array of samples and for each one obtain the value for a
// specific channel and store the value in a new array
for (CMData *sample in samples) {
[values addObject:sample.analogChannelData[channelIndex]];
}
// the maximum
NSNumber *maxValue = [values valueForKeyPath:#"#max.self"];
I want to replace this programming structure by a filter through an NSPredcicate or use valueForKeyPath: to obtain the maximum of the data I need.
Anyone knows how to do this without a for loop? Just using NSPredicates and/or valueForKeyPath?
Thank you very much in advance for your help.
Update 1
Finally I benckmarked the for-loop version against the keyPath version (see accepted answer) and it runs much faster so it is better to go with a for loop.
Recalling some lessons from my algorithms classes, I implemented an even faster version that doesn't need an array to store the values. I just iterate over the selected channel and just choose the maximum in each iteration. This is by far the fastest version.
So:
version 1: for loop (see code above)
version 2: version with custom property (see selected answer from Marcus, update 2)
version 3: new code
Code for version 3:
NSUInteger channelIndex = 31;
NSNumber *maxValue = #(-INFINITY);
for (CMTData *sample in samples) {
NSNumber *value = sample.analogChannelData[channelIndex];
if (value) { // I allow the possibility of NSNull values in the NSArray
if ([value compare:maxValue] == NSOrderedDescending)
maxValue = value;
}
}
// the maximum is in maxValue at the end of the loop
Performance:
After 20.000 iterations in iOS simulator:
Version 1: 12.2722 sec.
Version 2: 21.0149 sec.
Version 3: 5.6501 sec.
The decision is clear. I'll use the third version.
Update 2
After some more research, it is clear to me now that KVC does not work for infividual elements in the inner array. See the following links: KVC with NSArrays of NSArrays and Collection Accessor Patterns for To-Many Properties
Anyway because I wanted to compute the maximum of the elements it is better to iterate the array than use some tricks to make KVC work.
You can solve this with using Key Value Coding and the collection operators.
NSNumber *result = [sample valueForKeyPath:#"#max.analogDataChannel"];
Update 1
As Arcanfel mentioned, you can join the arrays together:
NSNumber *result = [samples valueForKeyPath:#"#max.#unionOfArrays.#analogChannelData"];
I would suggest reading the documentation that we both linked to. There are some very powerful features in there.
Update 2
Further to HRD's answer, he has your solution, you need to combine his changes with KVC.
Add a propert to your CMData object for currentChannel. Then you can call
[samples setValue:#(channelIndex) forKey:#"currentChannel"];
Which will set it in every instance in the array. Then call:
[samples valueForKeyPath:#"#max.analogDataForCurrentChannel"];
Then you are done.
I have not tested out the code yet, but I think this is exactly what you are looking for:
[samples valueForKeyPath:#"#max.(#unionOfArrays.analogChannelData)"];
I guess you can also use #distinctUnionOfArray to remove duplicate values.
Here is the link to Apple Documentation that covers collection operators.
Hope this is helpful!
Cheers!
A suggestion for further exploration only
Offhand it is not clear you can do this as-is with a single KVC operator. What you might consider is adding two properties to your class: currentChannel, which sets/gets the current channel; and analogChannelDataForCurrentChannel, which is equivalent to analogChannelData[currentChannel]. Then you can:
samples.currentChannel = channelIndex;
... [samples valueForKeyPath:"#max.analogChannelDataForCurrentChannel"];
with any appropriate locking between the two calls if thread-safety is required (so one thread does not set currentChannel, then a second, and then the first do the KVC operator with the second's channel...).
HTH
Currently I'm trying to find a compact way to average a matrix. The obvious solution is to sum the matrix, then divide by the number of elements. I have, however, come across a method on the apple developer website that claims this can be done in a simpler way using valueForKeyPath. This is linked here:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/CollectionOperators.html
Here is the example I'm currently working on to try and get it to work:
-(void)arrayAverager
{
NSMutableArray *myArray = [NSMutableArray arrayWithCapacity:25];
[myArray addObject:[NSNumber numberWithInteger:myValue]];
NSNumber *averageValue = [myArray valueForKeyPath:#"#avg.self"];
NSLog(#"avg = %#", averageValue);
}
The problem is: instead of averaging the array it merely prints out the elements in the array 1 by 1.
UPDATE
-(void) pixelAverager
{
//Put x-coordinate value of all wanted pixels into an array
NSMutableArray *xCoordinateArray = [NSMutableArray arrayWithCapacity:25];
[xCoordinateArray addObject:[NSNumber numberWithInteger:xCoordinate]];
NSLog(#"avg = %#", [xCoordinateArray valueForKeyPath:#"#avg.intValue"]);
}
You need to use #avg.floatValue (or #avg.doubleValue, or what have you). The #avg operator will average the property of the objects in the array specified by the name after the dot. The documentation is confusing on this point, but that is what:
to get the values specified by the property specified by the key path
to the right of the operator
Is saying. Since you have a collection of NSNumber objects, you use one of the *value accessors, e.g. floatValue to get the value of each object. As an example:
#include <Foundation/Foundation.h>
int main(void) {
NSMutableArray *ma = [NSMutableArray array];
[ma addObject:[NSNumber numberWithFloat:1.0]];
[ma addObject:[NSNumber numberWithFloat:2.0]];
[ma addObject:[NSNumber numberWithFloat:3.0]];
NSLog(#"avg = %#", [ma valueForKeyPath:#"#avg.floatValue"]);
return 0;
}
Compiling and running this code returns:
$ clang avg.m -framework Foundation -o avg
steve:~/code/tmp
$ ./avg
2013-01-18 12:33:15.500 avg[32190:707] avg = 2
steve:~/code/tmp
The nice thing about this approach is that this work for any collection, homogenous or otherwise, as long as all objects respond to the specified method, #avg will work.
EDIT
As pointed in the comments, the OP's problem is that he is averaging a collection with one element, and thus it appears to simply print the contents of the collection. For a collection of NSNumber objects, #avg.self works just fine.
No, you can't do like this. The object Transaction is a Modal Class. This class is having three properties, namely
payee
amount
date
Each row in this image represents one Transaction modal object.
transactions is an array which is holding all these rows (Transaction Modal Objects).
In these transactions array, they are trying to calculate the Transaction Modal amount field average using the operator #avg. So, its like
NSNumber *transactionAverage = [transactions valueForKeyPath:#"#avg.amount"];
your array doesn't have the key self. So that's the problem