views:

213

answers:

2

I have a class (from NSObject) that contains:

NSString name
int      position
float    speed

I then create an array (NSMutableArray) of objects from this class. I would like to then sort the array by the 'speed' value, which is a float.

I initially has the float value as an NSNumber and the int as NSInteger, and I was successfully sorting with:

[myMutableArray sortUsingFunction:compareSelector context:@selector(position)];

where myMutableArray is my array of objects.

here is the function:

static int compareSelector(id p1, id p2, void *context) {
    SEL methodSelector = (SEL)context;
    id value1 = [p1 performSelector:methodSelector];
    id value2 = [p2 performSelector:methodSelector];
    return [value1 compare:value2];
}

Now that I am using int instead of NSInteger, the above code does not work. Is there a more low-level command that I should be using to execute the sort? Thank!

+1  A: 
int compareSpeed( id p1 , id p2 , void *unused ) {
  return p1->speed > p2->speed ? 1 : p1->speed < p2->speed : -1 : 0;
}

Although really you should make the above a method of your class like so:

-(int) compareSpeed:(id)inOtherObjectOfSameType {
  return self->speed > inOtherObjectOfSameType->speed ? 1 : self->speed < inOtherObjectOfSameType->speed : -1 : 0;
}
drawnonward
drawnonward, Thank you! Your code looks like it should work, and I was hoping it would as it is more 'c'-like and less 'objective-c'-like, but when I integrate it into my class I get the compilation error: Request of member 'speed' is something not in a structure or union. After rehashing syntax and trying various tricks unsuccessfully, I settled on Isaac's code. I will revisit this problem and start from your suggestion on my next go-around.
J. Dave
You could expose speed as a property or method so you can access it with [yourItem speed] instead of yourItem->speed.
drawnonward
A: 

Similar to drawnonward, I'd suggest adding a comparison method to your class:

- (NSComparisonResult)compareSpeed:(id)otherObject
{
    if ([self speed] > [otherObject speed]) {
        return NSOrderedDescending;
    } else if ([self speed] < [otherObject speed]) {
        return NSOrderedAscending;
    } else {
        return NSOrdededSame;
    }
}

(You could collapse the if-else if-else using the ternary operator: test ? trueValue : falseValue, or if speed is an object with a compare: method (such as NSNumber), you could just return [[self speed] compare:[otherObject speed]];.)

You can then sort by

[myMutableArray sortUsingSelector:@selector(compareSpeed:)];

As suggested by Georg in a comment, you can also achieve your goal using NSSortDescriptor; if you're targeting 10.6, you can also use blocks and sortUsingComparator:(NSComparator)cmptr.

Isaac
Thanks Isaac! I was able to get the above working in my code. I cannot tell if there is much of a speed improvement over using NSInteger vs int, which is what prompted my initial question to begin with..., but this got me in the right direction.
J. Dave
My understanding from http://developer.apple.com/Mac/library/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_DataTypes/Reference/reference.html#//apple_ref/doc/uid/20000018-SW16 is that `NSInteger` is really `int` or `long`, depending on the system (via `typedef`), so there should be no performance difference between `NSInteger` and `int`. I tend to use `NSInteger` with Cocoa stuff so it reads like Cocoa to me, similar to how I used `NSComparisonResult`, `NSOrderedAscending`, `NSOrderedDescending`, and `NSOrderedSum` instead of the underlying C types/values.
Isaac
Oh, and to clarify, while `NSInteger` is basically a fancy name for a simple C type, `NSNumber` inherits from `NSObject` so it's a full Cocoa object. That's why I said with `NSNumber`, you could use its `compare:` method. `NSNumber` can also be stored in `NSArray`, `NSDictionary`, etc., while `NSInteger` cannot (since it's not an `NSObject`).
Isaac