I have some data like this :
1, 111, 2, 333, 45, 67, 322, 4445
NSArray *array = [[myData allKeys]sortedArrayUsingSelector: @selector(compare:)];
Sort and Simple Solution..
NSSortDescriptor *sortDescriptor;
sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"self"
ascending:YES
comparator:^(id obj1, id obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [montharray
sortedArrayUsingDescriptors:sortDescriptors];
[montharray removeAllObjects];
[montharray addObjectsFromArray:sortedArray];
NSLog(@"MONTH ARRAY :%@",montharray);
Implement your own method that returns NSComparisonResult
. It can be in a category if you wish.
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];
}
David's answer did the trick for me. For what it's worth, I want to share the Swift 1.0 version of the same answer.
extension NSString {
func psuedoNumericCompare(otherString: NSString) -> NSComparisonResult {
var left: NSString = self
var right: NSString = otherString
var leftNumber: Int = self.integerValue
var rightNumber: Int = otherString.integerValue
var leftScanner: NSScanner = NSScanner(string: left)
var rightScanner: NSScanner = NSScanner(string: right)
if leftScanner.scanInteger(&leftNumber) && rightScanner.scanInteger(&rightNumber) {
if leftNumber < rightNumber {
return NSComparisonResult.OrderedAscending
}
if leftNumber > rightNumber {
return NSComparisonResult.OrderedDescending
}
left = left.substringFromIndex(leftScanner.scanLocation)
right = right.substringFromIndex(rightScanner.scanLocation)
}
return left.caseInsensitiveCompare(right)
}
}
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.