Nested NSArray filtering - ios

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

Related

Can we create an array dynamically in ios?

I have a user input in my app, according to which, the number of arrays that I need to create changes (user inputs the number of channels into which I need to split an incoming data stream, with one array for each channel). The number of channels can be any multiple of 4 up to 32. I want to know whether there is a way to automatically initialize arrays, perhaps using for loops like in this pseudocode:
for(initialization, numberOfChannels, increment) {
NSString (set array name to channel%dArray, i);
NSMutableArray *channeliArray = [[NSAMutablerray alloc] init];
}
Everywhere I've looked, I didn't find any way to set the name of the array dynamically and hence allow initializations like the one above. Even using a 2D array would have the same problem. Any help is appreciated. Thank you!
Just use a two dimensional array as per #Jonah's comments -
self.channels=[NSMutableArray new];
for (int i=0;i<numberOfChannels;i++) {
[self.channels addObject:[NSMutableArray new]];
}
Then you can access a particular channel's array by index -
NSMutableArray *channel=[self.channels objectAtIndex:channelNumber];
Or, if you wanted to use channel names as per your pseudocode, use a dictionary of arrays
self.channels=[NSMutableDictionary new];
for (int i=0;i<numberOfChannels;i++) {
[self.channels setObject:[NSMutableArray new] forKey:[NSString stringWithFormat:#"channel%dArray",i]];
}
Then you can access a particular channel's array by name -
NSMutableArray *channelArray=[self.channels objectForKey:#"channel3Array"];

Performing arithmetic on double

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.

I need to build a HashMap like, Map<Set, Set>. Is it possible?

My usecase here is, I need to search for a string within a set of strings(say synonyms). If I find the relevant search-word within the set of strings, then I will display the set of values associated with that set of strings. I am currently thinking of implementing it as a Map, but not sure, if that's even possible.
I am confused about the best data-structure that can be used in this scenario?
Thanks for the help.
EDIT
Replaced Array with Set as it made more sense
Scenario:
Set-1 = {jug,jar,bottle,cup}
Set-2 = {"Store water", "Store Juice", "Store Coffee"}
For any input I receive from either jug,jar,bottle,cup, I should return the properties associated with it from Set-2. In simple words, I need to return whole of Set-2 when any word from Set-1 matches. Hopefully, this will make my question clear.
you can subclass NSObject for that kind of behaviour.
#interface Synonyms : NSObject
#property NSArray *strings;
#property NSArray *values;
-(NSArray *)searchForWord:(NSString *)searchWord;
#end
#implementation Synonyms
-(NSArray *)searchForWord:(NSString *)searchWord{
BOOL found=NO;
//add code to search for that search term in strings array
if(found)
return self.values;
else
return nil;
}
#end
so, first create all these synonyms instances and store them in NSArray.
Assign strings and values array to each instance.
When user enters search-word, call searchForWord function for each element of array.
If found ,it returns not nil object.else no synonym.
PART 2:
If you are looking for below kind of behaviour, example below
Dictionary myDic;
myDic.Add("car", "automobile");
myDic.Add("car", "autovehicle");
myDic.Add("car", "vehicle");
myDic.Add("bike", "vehicle");
myDic.ListOSyns("car") // should return {"automobile","autovehicle","vehicle" ± "car"}
// but "bike" should NOT be among the words returned
Then you can use NSDictionary,
NSDictionary *dictionary =#{#"car":#[#"automobile",#"autovehicle",#"vehicle"],#"bike":#[#"vehicle"]};
NSArray *values =[dictionary objectForKey:#"search-term"];
if(values==nil)
//no result
else
return values;

Objective-C how to pull several random items out of NSArray without getting duplicates? [duplicate]

This question already has answers here:
Getting a random object from NSArray without duplication
(3 answers)
Closed 7 years ago.
I have an array of random properties I would like to assign to equipment within the game I'm developing.
The code that I use below is returning an NSArray. I'm interested if there's way to get item indices from that array without getting duplicate values. The obvious solution is to create a mutable array with the returned array, do random, remove item that was returned and loop until the number of items is received.
But is there a different way of getting X random items from NSArray without getting duplicates?
//get possible enchantments
NSPredicate *p = [NSPredicate predicateWithFormat:#"type = %i AND grade >= %i", kEnchantmentArmor,armor.grade];
NSArray* possibleEnchantments = [[EquipmentGenerator allEnchantmentDictionary] objectForKey:#"enchantments"];
//get only applicable enchantments
NSArray *validEnchantments = [possibleEnchantments filteredArrayUsingPredicate:p];
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray:validEnchantments];
NSDictionary* enchantment = nil;
if(mutableArray.count>0)
{
//got enchantments, assign number and intensity based on grade
for (int i = 0; i<3;i++)
{
enchantment = mutableArray[arc4random()%mutableArray.count];
[mutableArray removeObject:enchantment];
//create enchantment from dictionary and assign to item.
}
}
You can shuffle the array using one of the following techniques:
What's the Best Way to Shuffle an NSMutableArray?
Non repeating random numbers
Then, take the first X elements from the array.
Many years ago, I was working on card game and I realized that shuffling the deck was an inefficient way to get random cards. What I would do in your shoes is pick a random element, and then replace it with the element at the end of the array, like so:
#interface NSMutableArray (pickAndShrink)
- (id) pullElementFromIndex:(int) index // pass in your random value here
{
id pickedItem = [self elementAtIndex:index];
[self replaceObjectAtIndex:index withObject:[self lastObject]];
[self removeLastObject];
return pickedItem;
}
#end
The array will shrink by one every time you pull an element this way.
You could use a random number generator to pick a starting index, and then pick the subsequent indices based on some kind of math function. You would still need to loop depending on how many properties you want.
Eg:
-(NSMutableArray*)getRandomPropertiesFromArray:(NSArray*)myArray
{
int lengthOfMyArray = myArray.count;
int startingIndex = arc4random()%lengthOfMyArray;
NSMutableArray *finalArray = [[NSMutableArray alloc]init]autorelease];
for(int i=0; i<numberOfPropertiesRequired; i++)
{
int index = [self computeIndex:i usingStartingIndex:startingIndex origninalArray:myArray];
[finalArray addObject:[myArray objectAtIndex:index]];
}
return finalArray;
}
-(int)computeIndex:(int)index usingStartingIndex:(int)startingIndex
{
//You write your custom function here. This is just an example.
//You will have to write some code to make use you don't pick an Index greater than the length of your array.
int computedIndex = startingIndex + index*2;
return startingIndex;
}
EDIT: Even your computeIndex function could use randomness in picking the subsequent indices. Since you have a startingIndex, and another index, you could use that to offset your function so that you never pick a duplicate.
EDIT: If your array is very large, and the subset you need to pick is small, then rather than shuffle the entire array (maybe more expensive), you could use this method to pick the number of items you need. But if your array is small, or if the number of items you need to pick are almost the size of the array, then the godel9's solution is better.
You can use a mutable array and then remove them as the are selected, use something like random()%array.count to get a random index. If you don't want to modify the array then copy it with [array mutableCopy].

Find the average of an NSMutableArray with valueForKeyPath

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

Resources