How to find the median value of NSNumbers in an NSArray? - iphone

I'm trying to calculate the median of a (small) set of NSNumbers in an NSArray. Every object in the NSArray is a NSNumber.
Here is what I'm trying, but it's not working:
NSNumber *median = [smallNSArray valueForKeyPath:#"#median.floatValue"];

NSArray *sorted = [smallNSArray sortedArrayUsingSelector:#selector(compare:)]; // Sort the array by value
NSUInteger middle = [sorted count] / 2; // Find the index of the middle element
NSNumber *median = [sorted objectAtIndex:middle]; // Get the middle element
You can get fancier. For example, the median of a set with an even number of numbers is technically the average of the middle two numbers. You could also wrap this up into a neat one-line method in a category on NSArray:
#interface NSArray (Statistics)
- (id)median;
#end
#implementation NSArray (Statistics)
- (id)median
{
return [[self sortedArrayUsingSelector:#selector(compare:)] objectAtIndex:[self count] / 2];
}
#end

For anyone who has the unusual need for this function, here's a category method on NSArray that will work with both an odd and an even number of elements:
NSARRAY CATEGORY METHOD
- (float)median {
if (self.count == 1) return [self[0] floatValue];
float result = 0;
NSUInteger middle;
NSArray * sorted = [self sortedArrayUsingSelector:#selector(compare:)];
if (self.count % 2 != 0) { //odd number of members
middle = (sorted.count / 2);
result = [[sorted objectAtIndex:middle] floatValue];
}
else {
middle = (sorted.count / 2) - 1;
result = [[#[[sorted objectAtIndex:middle], [sorted objectAtIndex:middle + 1]] valueForKeyPath:#"#avg.self"] floatValue];
}
return result;
}
TEST
NSArray * singleElement = #[#1];
NSArray * oddNumberOfElements = #[#3, #5, #7, #12, #13, #14, #19, #20, #21, #22, #23, #29, #39, #40, #56];
NSArray * evenNumberOfElements = #[#3, #5, #7, #12, #13, #14, #19, #20, #21, #22, #23, #29, #40, #56];
NSLog(
#"oddNumberOfElements: %f, evenNumberOfElements: %f singleElement: %f",
[oddNumberOfElements median], [evenNumberOfElements median], [singleElement median]
);
//oddNumberOfElements: 20.000000, evenNumberOfElements: 19.500000 singleElement: 1.000000

Swift Extension
extension Array where Element: Comparable {
var median: Element {
return self.sort(<)[self.count / 2]
}
}

Related

Sort NSArray for a specific order

I have an NSArray of custom objects.
Each object contains one integer value for ex. 1,2,3,4
Now I want to sort Array like below
9 7 5 3 1 2 4 6 8
Could some one help me?
Here is your answer.
Hope your first array is in sorted order (ascending) if not then you need to sort it first.
NSMutableArray *myArray = [NSMutableArray array];
//Populate your array with custom objects. I had created my own which contain an integer type property.
[myArray addObject:[[myObject alloc] initWithId:11 objectName:#"K"]];
[myArray addObject:[[myObject alloc] initWithId:3 objectName:#"C"]];
[myArray addObject:[[myObject alloc] initWithId:4 objectName:#"D"]];
....
...
.... and so on
then sort it in Ascending order. You can do it Descending also but then you need to change the below logic little bit. I showing with Ascending order.
NSArray *tempArray = [myArray sortedArrayUsingComparator:^NSComparisonResult(myObject *obj1, myObject *obj2) {
if([obj1 objectId] > [obj2 objectId]) return NSOrderedDescending;
else if([obj1 objectId] < [obj2 objectId]) return NSOrderedAscending;
else return NSOrderedSame;
}];
Now sort them as per your requirement
NSMutableArray *finalArray = [NSMutableArray arrayWithArray:tempArray];
NSInteger totalObjects = [tempArray count];
NSInteger centerObjectIndex = totalObjects>>1;
__block NSInteger rightPosition = centerObjectIndex + 1;
__block NSInteger leftPosition = centerObjectIndex - 1;
__block BOOL toggle = NO;
[tempArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if(idx == 0) [finalArray replaceObjectAtIndex:centerObjectIndex withObject:obj];
else
{
if(toggle)
{
if(leftPosition >= 0)
{
[finalArray replaceObjectAtIndex:leftPosition withObject:obj];
leftPosition -= 1;
}
}
else
{
if(rightPosition < totalObjects)
{
[finalArray replaceObjectAtIndex:rightPosition withObject:obj];
rightPosition += 1;
}
}
toggle = !toggle;
}
}];
Here is the final step if your array contains an even numbers of objects
if(!(totalObjects % 2))
{
[finalArray removeObjectAtIndex:0];
[finalArray addObject:[tempArray objectAtIndex:totalObjects-1]];
}
Now you are at end. Your array named finalArray get sorted as per your requirement.

Min and Max Value of an NSMutableArray

I have a simple looking piece of code that has me completely flummoxed.
NSInteger ymax;
NSInteger ymin;
NSInteger numberIndex1;
NSInteger numberIndex2;
for (NSNumber *theNumber in array2)
{
if ([theNumber integerValue] > ymax) {
ymax = [theNumber integerValue];
numberIndex1 = [array2 indexOfObject:theNumber];
}
}
for (NSNumber *theNumber in array2)
{
if ([theNumber integerValue] < ymin) {
ymin = [theNumber integerValue];
numberIndex2 = [array2 indexOfObject:theNumber];
}
}
NSLog(#"Highest number: %d at index: %d", ymax, numberIndex1);
NSLog(#"Lowest number: %d at index: %d", ymin, numberIndex2);
The NSLog is outputted as:
Highest number: 129171656 at index: -1073752392 (Huh??)
Lowest number: 57 at index: 5 (Correct)
How do you explain this odd behaviour? Both the functions look the same. One is working and one isn't? I've played around a lot with this, but I still can't put my finger on it. Any help would be appreciated/
You can get maximum and minimum number as below code. It may help you
NSNumber * max = [array2 valueForKeyPath:#"#max.intValue"];
NSNumber * min = [array2 valueForKeyPath:#"#min.intValue"];
NSUInteger numberIndex1 = [array indexOfObject:min];
NSUInteger numberIndex2 = [array indexOfObject:max];
NSLog(#"Max Value = %d and index = %d",[max intValue],numberIndex1);
NSLog(#"Min Value = %d and index = %d",[min intValue],numberIndex2);
If I am not wrong you are considering the default value of NSInteger is 0, No, it isn't guaranteed to be zero, since it's a local automatic variable. Without initialization, its value is indeterminate.
so you need to set default values for your var, start with ymax = -1;
Please initialize NSInteger ymax = 0;
NSInteger ymin = 0 ;
NSInteger numberIndex1 = 0;
NSInteger numberIndex2 = 0;
It will fix your issue.
Otherwise it is checking with a garbage value and giving wrong result.
enjoy with my answer.......happy coding
NSArray *arr1=[[NSArray alloc]initWithObjects:#"0.987",#"0.951",#"0.881",#"0.784",#"0.662",#"0.522",#"0.381",#"-0.265",#"-0.197",
#"0.189",#"-0.233",#"0.310",#"0.402",#"0.402",#"0.988",#"0.633",#"0.661",#"0.656",#"0.617",#"0.634",#"0.690",#"0.767",#"0.836",nil];
NSNumber * max = [arr1 valueForKeyPath:#"#max.floatValue"];
NSString *str=[NSString stringWithFormat:#"%#",max];
NSInteger path=[arr1 indexOfObject:str];
NSIndexPath *indepath=[NSIndexPath indexPathForItem:path inSection:0];
NSNumber * min = [arr1 valueForKeyPath:#"#min.floatValue"];
NSString *str1=[NSString stringWithFormat:#"%#",min];
NSInteger path1=[arr1 indexOfObject:str1];
NSIndexPath *indepath1=[NSIndexPath indexPathForItem:path1 inSection:0];
NSLog(#"Max Value = %f and index = %ld",[max floatValue],(long)indepath.row);
NSLog(#"Min Value = %f and index = %ld",[min floatValue],(long)indepath1.row);
A more legitimate solution would be:
NSArray *originalArray = #[[NSNumber numberWithInt:91],
[NSNumber numberWithInt:12],
[NSNumber numberWithInt:99123],
[NSNumber numberWithInt:9],
[NSNumber numberWithInt:43234]];
NSArray *sortedArray = [originalArray sortedArrayUsingSelector:#selector(compare:)];
NSNumber *minNumber = [sortedArray objectAtIndex:0];
NSNumber *maxNumber = [sortedArray lastObject];
NSInteger minIndex = [originalArray indexOfObject:minNumber];
NSInteger maxIndex = [originalArray indexOfObject:maxNumber];

Sort a NSMutableArray of location with my GPS position

I want to sort a NSMutableArray, where each row is a NSMutableDictionary, with my GPS position from CoreLocation framework.
This is an example of my array of POI
arrayCampi = (
{
cap = 28100;
"cell_phone" = "";
championship = "IBL 1D";
citta = Novara;
division = "";
email = "";
fax = 0321457933;
indirizzo = "Via Patti, 14";
latitude = "45.437174";
league = "";
longitude = "8.596029";
name = "Comunale M. Provini";
naz = Italy;
prov = NO;
reg = Piemonte;
sport = B;
surname = "Elettra Energia Novara 2000";
telefono = 03211816389;
webaddress = "http://www.novarabaseball.it/";
})
I need to sort this array with my location (lat and long) with field 'latitude' and 'longitude' of each row in ascending mode (first row is POI nearest to me).
I have tried this solution without success:
+ (NSMutableArray *)sortBallparkList:(NSMutableArray *)arrayCampi location:(CLLocation *)myLocation {
if ([arrayCampi count] == 0) {
return arrayCampi;
}
if (myLocation.coordinate.latitude == 0.00 &&
myLocation.coordinate.longitude == 0.00) {
return arrayCampi;
}
NSMutableArray *sortedArray = [NSMutableArray arrayWithArray:arrayCampi];
BOOL finito = FALSE;
NSDictionary *riga1, *riga2;
while (!finito) {
for (int i = 0; i < [sortedArray count] - 1; i++) {
finito = TRUE;
riga1 = [sortedArray objectAtIndex: i];
riga2 = [sortedArray objectAtIndex: i+1];
CLLocationDistance distanceA = [myLocation distanceFromLocation:
[[CLLocation alloc]initWithLatitude:[[riga1 valueForKey:#"latitude"] doubleValue]
longitude:[[riga1 valueForKey:#"longitude"] doubleValue]]];
CLLocationDistance distanceB = [myLocation distanceFromLocation:
[[CLLocation alloc]initWithLatitude:[[riga2 valueForKey:#"latitude"] doubleValue]
longitude:[[riga2 valueForKey:#"longitude"] doubleValue]]];
if (distanceA > distanceB) {
[riga1 retain];
[riga2 retain];
[sortedArray replaceObjectAtIndex:i+1 withObject:riga2];
[sortedArray replaceObjectAtIndex:i withObject:riga1];
[riga1 release];
[riga2 release];
finito = FALSE;
}
}
}
return sortedArray;
}
Can anyone help me, also with other solution?
Alex.
Sorting by lat and long will not give you the nearest location from any given coordinates. As an approximation*) you could use Pythagoras (you learned that in high school, remember?):
float distance = sqrtf(powf((origLat-destLat),2)+powf((origLon-destLon), 2));
Simply add that to your dictionary with key #"distance" and sort with
NSArray *sorted = [arrayOfDictionaries sortedArrayUsingDescriptors:
#[[NSSortDescriptor sortDescriptorWithKey:#"distance" ascending:YES]]];
*) It's an approximation because theoretically distance between two points is a curved line on the surface of an ellipsoid.
[arrayCampi sortedArrayUsingSelector:#selector(compare:)];
- (NSComparisonResult)compare:(NSDictionary *)otherObject {
if ([[self objectForKey:#"key"] isEqual:[otherObject objectForKey:#"key"]]) {
return NSOrderedSame;
}
else if (//condition) {
return NSOrderedAscending;
}
else {
return NSOrderedDescending;
}
}
Take a look at How to sort an NSMutableArray with custom objects in it?
I think there's no need to implement your own sorting algorithm. There are the ready ones out there :-) I would suggest to look at NSSortDescriptor.
And since you keep your geo coordinates in NSString format, and not the NSNumber, you probably would need to write your own NSPredicate for NSString objects comparison in your class. (I don't remember if #"123" is greater than #"1.23", I mean special symbol '.')

Sharing Struct Information Between Classes - Objective C - OpenGL ES

I have an OpenGL ES based app with the following structure :
I parse vertices from a DAE file which contains a number of objects (using NSXMLParser).
As I parse each set of vertices, I create an object (sceneObject) and set the vertices array in this object to match the parsed vertices. I am then planning to add these sceneObjects to array of objects to be rendered by OpenGL ES.
Initially I had taken an approach of using NSArrays to store the vertice data, however I understand that the OpenGL commands (such as glvertexpointer) do not accept NSObject values, bue instead need raw values (glfloats and gluints etc).
Rather than convert the NSObjects back to GL raw values within the object class, I am now trying to take a struct approach. This is the code where I parse the vertice information :
if (floatArray) {
if (currentlyParsing == kVerticeInformation) {
if (currentParserTagType == kGeometry) {
//NSLog(#"Loaded Vertices %#", string);
NSArray * nums = [string componentsSeparatedByString:#" "];
culmunativeCount += [nums count];
//NSLog(#"Culm Count is %d", culmunativeCount);
[fullParseString appendString : string];
if (checkSumCount <= culmunativeCount) {
//NSLog(#"Full Parse String is %#", fullParseString);
NSArray * finishedArray = [fullParseString componentsSeparatedByString:#" "];
//NSLog(#"Finihsed ARray is %d", [finishedArray count]);
[finishedParsingArray addObjectsFromArray:finishedArray];
//NSLog(#" FINISHED PARSING = %d", [finishedParsingArray count]);
NSUInteger baseIndex;
for (baseIndex=0; baseIndex <[finishedParsingArray count]; baseIndex +=3) {
NSString * xPoint = [finishedParsingArray objectAtIndex:baseIndex];
NSDecimalNumber * errorCheckX = [NSDecimalNumber decimalNumberWithString:xPoint];
float x = [errorCheckX floatValue];
NSString * yPoint = [finishedParsingArray objectAtIndex:baseIndex +1];
NSDecimalNumber * errorCheckY = [NSDecimalNumber decimalNumberWithString:yPoint];
float y = [errorCheckY floatValue];
NSString * zPoint = [finishedParsingArray objectAtIndex:baseIndex+2];
NSDecimalNumber * errorCheckZ = [NSDecimalNumber decimalNumberWithString:zPoint];
float z = [errorCheckZ floatValue];
Vertex3D vertexItem = {x,y,z}
//NSLog(#"Vertices stored are %f, %f, %f", x,y,z);
}
currentlyParsing = kNilSetting;
checkSumCount = 0;
[finishedParsingArray removeAllObjects];
//[finishedArray release];
culmunativeCount = 0;
[fullParseString setString:#""];
}
}
}
}
As you can see, I am now creating a Vertex3D C Struct each time with the parsed vertice information.
My questions are :
How would I then create an array of these individual C Vertex3D structs ?
How can I then pass this array of structs to my sceneObject ?
Create Objective C wrapper class for your array. It can hold several arrays (e.g. vertices, normals and tangents). You know the array size beforehand, so you can preallocate the required amount of space:
#interface MYVertexArray : NSObject {
float* _mVerts;
float* _mNormals;
int _mCount;
}
#property(nonatomic, readonly) float* verts;
#property(nonatomic, readonly) float* norms;
/**
Here you're allocating your arrays (don't forget to free them is dealloc).
*/
-(id) initWithSize:(int) size;
#end
and then in cycle, you're just doing this:
MYVertexArray* my_vertex_data = [[MYVertexArray alloc] initWithSize: [finishedParsingArray count]];
for(int i=0; i<[finishedParsingArray count]; ++i) {
my_vertex_data.verts[i] = [[finishedParsingArray objectAtIndex: i] floatValue];
}

Finding the maximum elements in an NSArray (or NSMutableArray)

I am having a bit of trouble navigating around an NSArray.
My array:
Element[0] = "ElementA"
Element[1] = "ElementA"
Element[2] = "ElementA"
Element[3] = "ElementA"
Element[4] = "ElementB"
Element[5] = "ElementC"
Are there any methods in Objective-C that will help me find the "median" element? In this case, the "median" would be "ElementA", or the value that occurs the maximum number of times.
In C# this would be a single call, but I can't find an equivalent in Objective-C.
Many thanks,
Brett
Here's how I'd do it:
NSArray * elements = ...; //your array of elements:
NSCountedSet * counts = [NSCountedSet setWithArray:elements]:
id modeObject = nil;
NSUInteger modeCount = 0;
for (id element in counts) {
if ([counts countForObject:element] > modeCount) {
modeCount = [counts countForObject:element];
modeObject = element;
}
}
NSLog(#"element with highest frequency: %#", modeObject);
An NSCountedSet is an NSMutableSet that also remembers how many times its elements have been added to the array.
Wrote this just for you :)
- (NSString *) findModeString: (NSArray *) array {
NSMutableDictionary *stats = [[NSMutableDictionary alloc] init];
for(NSString *str in array) {
if(![stats objectForKey:str]) {
[stats setObject: [NSNumber numberWithInt:1] forKey:str];
} else {
[stats setObject: [NSNumber numberWithInt:[[stats objectForKey:str] intValue] + 1] forKey:str];
}
}
NSInteger maxOccurrences = 0;
NSString *max;
for(NSString *key in stats) {
if([[stats objectForKey:key] intValue] > maxOccurrences) {
max = key;
maxOccurrences = [[stats objectForKey:key] intValue];
}
}
[stats release];
return max;
}
EDIT: Although my solution works, you should upvote/accept #Dave DeLong's answer, it is much much better.
Couldn't you just use:
[myarray length] /2