Finding smallest and biggest value in NSArray of NSNumbers - ios

What's an effective and great way to compare all the values of NSArray that contains NSNumbers from floats to find the biggest one and the smallest one?
Any ideas how to do this nice and quick in Objective-C?

If execution speed (not programming speed) is important, then an explicit loop is the fastest. I made the following tests with an array of 1000000 random numbers:
Version 1: sort the array:
NSArray *sorted1 = [numbers sortedArrayUsingSelector:#selector(compare:)];
// 1.585 seconds
Version 2: Key-value coding, using "doubleValue":
NSNumber *max=[numbers valueForKeyPath:#"#max.doubleValue"];
NSNumber *min=[numbers valueForKeyPath:#"#min.doubleValue"];
// 0.778 seconds
Version 3: Key-value coding, using "self":
NSNumber *max=[numbers valueForKeyPath:#"#max.self"];
NSNumber *min=[numbers valueForKeyPath:#"#min.self"];
// 0.390 seconds
Version 4: Explicit loop:
float xmax = -MAXFLOAT;
float xmin = MAXFLOAT;
for (NSNumber *num in numbers) {
float x = num.floatValue;
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}
// 0.019 seconds
Version 5: Block enumeration:
__block float xmax = -MAXFLOAT;
__block float xmin = MAXFLOAT;
[numbers enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
float x = num.floatValue;
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}];
// 0.024 seconds
The test program creates an array of 1000000 random numbers and then applies all sorting
techniques to the same array. The timings above are the output of one run, but I make about 20 runs with very similar results in each run. I also changed the order in which the 5 sorting methods are applied to exclude caching effects.
Update: I have now created a (hopefully) better test program. The full source code is here: https://gist.github.com/anonymous/5356982. The average times for sorting an
array of 1000000 random numbers are (in seconds, on an 3.1 GHz Core i5 iMac, release compile):
Sorting 1.404
KVO1 1.087
KVO2 0.367
Fast enum 0.017
Block enum 0.021
Update 2: As one can see, fast enumeration is faster than block enumeration (which is also stated here: http://blog.bignerdranch.com/2337-incremental-arrayification/).
EDIT: The following is completely wrong, because I forgot to initialize the object used as lock, as Hot Licks correctly noticed, so that no synchronization is done at all.
And with lock = [[NSObject alloc] init]; the concurrent enumeration is so slow
that I dare not to show the result. Perhaps a faster synchronization mechanism might
help ...)
This changes dramatically if you add the NSEnumerationConcurrent option to the
block enumeration:
__block float xmax = -MAXFLOAT;
__block float xmin = MAXFLOAT;
id lock;
[numbers enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
float x = num.floatValue;
#synchronized(lock) {
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}
}];
The timing here is
Concurrent enum 0.009
so it is about twice as fast as fast enumeration. The result is probably not representative
because it depends on the number of threads available. But interesting anyway! Note that I
have used the "easiest-to-use" synchronization method, which might not be the fastest.

Save float by wrapping under NSNumber then
NSNumber *max=[numberArray valueForKeyPath:#"#max.doubleValue"];
NSNumber *min=[numberArray valueForKeyPath:#"#min.doubleValue"];
*Not compiled and checked, already checked with intValue, not sure about double or float

sort it. take the first and the last element.
btw: you cant store floats in an NSArray, you will need to wrap them in NSNumber objects.
NSArray *numbers = #[#2.1, #8.1, #5.0, #.3];
numbers = [numbers sortedArrayUsingSelector:#selector(compare:)];
float min = [numbers[0] floatValue];
float max = [[numbers lastObject] floatValue];

I agree with sorting the array then picking the first and last elements, but I find this solution more elegant (this will also work for non numeric objects by changing the comparison inside the block):
NSArray *unsortedArray = #[#(3), #(5), #(1)];
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSNumber *item1 = (NSNumber *)obj1;
NSNumber *item2 = (NSNumber *)obj2;
return [item1 compare:item2];
}];
If you really want to get fancy and have a really long list and you don't want to block your main thread, this should work:
NSComparator comparison = ^NSComparisonResult(id obj1, id obj2) {
NSNumber *item1 = (NSNumber *)obj1;
NSNumber *item2 = (NSNumber *)obj2;
return [item1 compare:item2];
};
void(^asychSort)(void) = ^
{
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:comparison];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"Finished Sorting");
//do your callback here
});
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), asychSort);

Made simple
NSArray *numbers = #[#2.1, #8.1, #5.0, #.3];
numbers = [numbers sortedArrayUsingSelector:#selector(compare:)];
float min = [numbers[0] floatValue];
float max = [[numbers lastObject] floatValue];
NSLog(#"MIN%f",min);
NSLog(#"MAX%f",max);

Related

Equivalent of Swift Dotted Loop and Map in Objective C

I have this code in swift
animation.keyTimes = (0...count).map {
NSNumber(value: CFTimeInterval($0) / CFTimeInterval(count))
}
I want to convert it into objective C. I am not sure how to use triple dot operator in objective C along with the map.
any help would be appreciated.
From docs:
The closed range operator (a...b) defines a range that runs from a to b, and includes the values a and b. The value of a must not be greater than b.
So, i believe this should work fine:
NSMutableArray *result = [NSMutableArray array];
for (NSInteger i = 0; i <= count; i++) {
[result addObject: [NSNumber numberWithInteger:i]];
}
animation.keyTimes = result
This dot notation and mapping doesn't exist as first class citizens in Objective-C.
You could try Ranges (NSMakeRange(0, count);) and then enumerate the range.
Or you can write your own map function on a range or an array and use that.
... operator and .map method are available in swift only. So you can do it in objective-c like given below.
NSMutableArray *keyTimes = [NSMutableArray array];
for(int i = 0; i <= count; i++) {
[keyTimes addObject:[NSNumber numberWithInteger: CFTimeInterval(i)/CFTimeInterval(count)];
}
animation.keyTimes = keyTimes
There is no direct equivalent for the closed range operator and map in Objective-C.
An analogous translation is NSRange, NSIndexSet and enumerateIndexes...
NSInteger startIndex = 0;
NSInteger count = 100;
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(startIndex, count + 1)];
__block NSMutableArray<NSNumber *> * keyTimes = [NSMutableArray array];
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[keyTimes addObject: [NSNumber numberWithDouble: (double)idx / (double)count]];
}];
NSLog(#"%#", keyTimes);

Inputted values from textfield to NSArray

I have a problem creating a solution to this problem:
Create an app that will store 5 numbers (preferrably float) and then sort them out.The array type is immutable.
First off: My problem is how to get the floats from the textfield then put it into a array. My idea is to code it like this:
int a = [num1.text intValue];
int b = [num2.text intValue];
int c = [num3.text intValue];
NSArray *myArray;
myArray = [NSArray stringWithFormat: #"%f",a,b,c];
My second problem is that I can't understand how to sort the floats. Will you please give me some idea?
Thank you very much!
Something like this should work:
CGFloat a = [num1Label.text floatValue];
CGFloat b = [num2Label.text floatValue];
CGFloat c = [num3Label.text floatValue];
CGFloat d = [num4Label.text floatValue];
CGFloat e = [num5Label.text floatValue];
NSArray *array = #[ [NSNumber numberWithFloat:a],
[NSNumber numberWithFloat:b],
[NSNumber numberWithFloat:c],
[NSNumber numberWithFloat:d],
[NSNumber numberWithFloat:e]];
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
// logic for sorting
NSNumber *number1 = (NSNumber *)obj1;
NSNumber *number2 = (NSNumber*)obj2;
return [first compare:second];
}];
CGFloat is basically the same as float.
If you check the Documentation (OPTION + Click on that), you see this:
# define CGFLOAT_TYPE float
// ...
typedef CGFLOAT_TYPE CGFloat;
CG comes from Core Graphics.
float comes from C/C++
It is more recommended to use CGFloat in Objective-C, instead of simply float. Also, NSInteger instead of int.

How to find the smallest and biggest object of an NSMutableArray

Hi I am trying to make it so that I can find and log the biggest object of an NSMutableArray. I have searched for this, but I have only seen it for an NSArray. This is the code I saw.
numbers = [numbers sortedArrayUsingSelector:#selector(compare:)];
float min = [numbers[0] floatValue]
float max = [[numbers lastObject] floatValue];
However, when I put that in, it doesn't work because I am using a mutable array.
Thanks!
It works for mutable array.
NSArray *sortedNumbers = [numbers sortedArrayUsingSelector:#selector(compare:)];
float min = [sortedNumbers[0] floatValue]
float max = [[sortedNumbers lastObject] floatValue];

How does NSMutableArray achieve such high speed in fast enumeration

The y axis represents the the average access time (in ns) to each node in the list/array (total time to access all elements divided by the number of elements).
The x axis represents the number of elements in the array being iterated over.
Where red is an implementation of NSMutableArray and blue is my linked list (CHTape).
In each outer loop each list/array has a empty string #"" appended to it. In the inner loops each string in each list/array is retrieved, this is timed and recorded. After everything the times our outputted in a Wolfram Language output to produce a plot.
How does NSMutableArray achieve such amazing and consistent results? How can one achieve similar?
My NSFastEnumeration Implementation:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])stackBuffer count:(NSUInteger)len
{
if (state->state == 0)
{
state->state = 1;
state->mutationsPtr = &state->extra[1];
state->extra[0] = (unsigned long)head;
}
CHTapeNode *cursor = (__bridge CHTapeNode *)((void *)state->extra[0]);
NSUInteger i = 0;
while ( cursor != nil && i < len )
{
stackBuffer[i] = cursor->payload;
cursor = cursor->next;
i++;
}
state->extra[0] = (unsigned long)cursor;
state->itemsPtr = stackBuffer;
return i;
}
Complete Testing Code:
NSMutableArray *array = [NSMutableArray array];
CHTape *tape = [CHTape tape];
unsigned long long start;
unsigned long long tapeDur;
unsigned long long arrayDur;
NSMutableString * tapeResult = [NSMutableString stringWithString:#"{"];
NSMutableString * arrayResult = [NSMutableString stringWithString:#"{"];
NSString *string;
int iterations = 10000;
for (int i = 0; i <= iterations; i++)
{
[tape appendObject:#""];
[array addObject:#""];
// CHTape
start = mach_absolute_time();
for (string in tape){}
tapeDur = mach_absolute_time() - start;
// NSArray
start = mach_absolute_time();
for (string in array){}
arrayDur = mach_absolute_time() - start;
// Results
[tapeResult appendFormat:#"{%d, %lld}", i, (tapeDur/[tape count])];
[arrayResult appendFormat:#"{%d, %lld}", i, (arrayDur/[array count])];
if ( i != iterations)
{
[tapeResult appendString:#","];
[arrayResult appendString:#","];
}
}
[tapeResult appendString:#"}"];
[arrayResult appendString:#"}"];
NSString *plot = [NSString stringWithFormat:#"ListPlot[{%#, %#}]", tapeResult, arrayResult];
NSLog(#"%#", plot);
By forcing ARC off on the link list related files efficiency increased dramatically. It reduced access time from ~70ns to ~14ns. While this is still slower, on average, then NSArray its only, on average, about two times slower, as opposed to ten times slower.
While ARC can make some code faster, in iterative situations adds unnecessary release/retain calls.
Discovered thanks to Greg Parker's comment.

how to round off NSNumber and create a series of number in Object C

May i know how to round up a NSNumber in object C ?
for(int i=6; i>=0; i--)
{
DayOfDrinks *drinksOnDay = [appDelegate.drinksOnDayArray objectAtIndex:i];
NSString * dayString= [NSDate stringForDisplayFromDateForChart:drinksOnDay.dateConsumed];
[dayArray addObject:dayString];//X label for graph the day of drink.
drinksOnDay.isDetailViewHydrated = NO;
[drinksOnDay hydrateDetailViewData];
NSNumber *sdNumber = drinksOnDay.standardDrinks;
[sdArray addObject: sdNumber];
}
inside this sdArray are all the numbers like this 2.1, 1.3, 4.7, 3.1, 4.8, 15.1, 7.2;
i need to plot a graph Y axis so i need a string of whatever is from the NSNumber to show
a NSString of this {#"0", #"2", #"4", #"6", #"8", #"10", #"12",#"14",#"16"}. as i need to start from zero, i need to determine which is the biggest number value. in this case it will be 15.1 to show 16 in the graph. instead of doing a static labeling, i will like to do a dynamic labeling.
what you have seen in the graph is static numbering not dynamic.
i'm sorry for missing out the important info.
thanks for all the comments
Check out the math functions in math.h, there's some nice stuff there like
extern double round ( double );
extern float roundf ( float );
As for the rest of your question you probably have to parse your strings into numbers, perform whatever action you want on them (rounding, sorting, etc) then put them back into strings.
Your edit now makes a lot more sense. Here is an example of getting all even numbers from your 0 to your max value in your NSArray of NSNumbers (assuming all values are positive, could be changed to support negative values by finding minimum float value as well).
NSArray *sdArray = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:2.1],
[NSNumber numberWithFloat:1.3],
[NSNumber numberWithFloat:4.7],
[NSNumber numberWithFloat:3.1],
[NSNumber numberWithFloat:4.8],
[NSNumber numberWithFloat:15.1],
[NSNumber numberWithFloat:7.2],
nil];
//Get max value using KVC
float fmax = [[sdArray valueForKeyPath:#"#max.floatValue"] floatValue];
//Ceiling the max value
int imax = (int)ceilf(fmax);
//Odd check to make even by checking right most bit
imax = (imax & 0x1) ? imax + 1 : imax;
NSMutableArray *array = [NSMutableArray arrayWithCapacity:(imax / 2) + 1];
//Assuming all numbers are positive
//(should probably just use unsigned int for imax and i)
for(int i = 0; i <= imax; i +=2)
{
[array addObject:[NSString stringWithFormat:#"%d", i]];
}
NSLog(#"%#", array);

Resources