Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to let the sortedArrayUsingSelector using integer to sort instead of String?

I have some data like this :

1, 111, 2, 333, 45, 67, 322, 4445

NSArray *array = [[myData allKeys]sortedArrayUsingSelector: @selector(compare:)];

If I run this code, it sorted like this:

1, 111, 2,322, 333, 4445, 45, 67,

but I actually want this:

1, 2, 45, 67, 111, 322, 333, 4445

How can I implement it? thz u.

like image 451
Tattat Avatar asked May 02 '10 09:05

Tattat


2 Answers

Expanding on Paul Lynch's answer, here's an example I have doing exactly this using a comparison method as a category on NSString. This code handles only the case of numbers followed by optional non-numeric qualifiers, but you could extend it to handle cases like "1a10" etc. if desired.

Once you create the category method, you just need to do

[[myData allKeys]sortedArrayUsingSelector:@selector(psuedoNumericCompare:)];

@interface NSString (Support) 
- (NSComparisonResult) psuedoNumericCompare:(NSString *)otherString;
@end

@implementation NSString (Support) 

// "psuedo-numeric" comparison
//   -- if both strings begin with digits, numeric comparison on the digits
//   -- if numbers equal (or non-numeric), caseInsensitiveCompare on the remainder

- (NSComparisonResult) psuedoNumericCompare:(NSString *)otherString {

    NSString *left  = self;
    NSString *right = otherString;
    NSInteger leftNumber, rightNumber;


    NSScanner *leftScanner = [NSScanner scannerWithString:left];
    NSScanner *rightScanner = [NSScanner scannerWithString:right];

    // if both begin with numbers, numeric comparison takes precedence
    if ([leftScanner scanInteger:&leftNumber] && [rightScanner scanInteger:&rightNumber]) {
        if (leftNumber < rightNumber)
            return NSOrderedAscending;
        if (leftNumber > rightNumber)
            return NSOrderedDescending;

        // if numeric values tied, compare the rest 
        left = [left substringFromIndex:[leftScanner scanLocation]];
        right = [right substringFromIndex:[rightScanner scanLocation]];
    }

    return [left caseInsensitiveCompare:right];
}
like image 106
David Gelhar Avatar answered Nov 15 '22 08:11

David Gelhar


You can use NSString's -[compare:options:] function and the NSNumericSearch option to compare NSStrings numerically, without having to convert them to NSIntegers first (which can be quite expensive, especially in longer loops).

Since you want to use an NSArray, you can use NSSortDescriptor's +[sortDescriptorWithKey:ascending:comparator:] (or the identical -initWithKey:ascending:comparator: if you want a pre-retained object) function to do block-based comparisation like this:

[NSSortDescritor sortDescriptorWithKey:@"myKey"
                             ascending:NO
                            comparator:^(id obj1, id obj2)
    {
        return [obj1 compare:obj2 options:NSNumericSearch];
    }
];

Sorting using this method will give the same results as David's answer, but without having to deal with NSScanner yourself.

like image 41
liclac Avatar answered Nov 15 '22 07:11

liclac