Abhinav
Abhinav

Reputation: 38162

Sorting NSString values as if NSInteger using NSSortDescriptor

I have created a sort descriptor to sort a plist response coming from my server. This works well with sort key having values upto 9. With more than 10 items I see abrupt results with sort key arranged in the order = 1, 10, 11, 2, 3, 4, 5, 6, 7, 8, 9

NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sort" ascending:YES];
self.myList = [NSMutableArray arrayWithArray:[unsortedList sortedArrayUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]]];

How to make it arrange in the correct order of 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11?

Upvotes: 28

Views: 16740

Answers (7)

starfall
starfall

Reputation: 1

input

{
    "seats": [{
        "seatNumber": "1"
    }, {
        "seatNumber": "10"
    }, {
        "seatNumber": "2"
    }]
}

sort using [NSSortDescriptor sortDescriptorWithKey:@"seatNumber" ascending:YES selector:@selector(localizedStandardCompare:)]]. The key is to use localizedStandardCompare. This will solve you problem.

NSArray *seats = [self.seats sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"seatNumber" ascending:YES selector:@selector(localizedStandardCompare:)]]];

output

{
    "seats": [{
        "seatNumber": "1"
    }, {
        "seatNumber": "2"
    }, {
        "seatNumber": "10"
    }]
}

documentation: https://developer.apple.com/documentation/foundation/nsstring/1409742-localizedstandardcompare

Upvotes: 0

Subash Varadhan
Subash Varadhan

Reputation: 11

All the above methods need good knowledge of basics to implement; but for the newbies I suggest the simplest way – to use the native block method

NSArray* sortedArr =[fetchResults sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {

    int aValue = [[a valueForKey:@"subgroupId"] intValue];
    int bValue = [[b valueForKey:@"subgroupId"] intValue];

    return aValue > bValue;
}];

Upvotes: 0

FluffulousChimp
FluffulousChimp

Reputation: 9185

NSMutableArray *list = [NSMutableArray arrayWithObjects:@"11",@"2",@"3",@"1", nil];
[list sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    NSInteger firstInteger = [obj1 integerValue];
    NSInteger secondInteger = [obj2 integerValue];
    if( firstInteger > secondInteger) return NSOrderedDescending;
    if( firstInteger == secondInteger) return NSOrderedSame;
    return NSOrderedAscending; // edited
}];

No guarantees about performance

Upvotes: 7

lucidbeing
lucidbeing

Reputation: 214

NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sort.intValue" ascending:YES];
self.myList = [NSMutableArray arrayWithArray:[unsortedList sortedArrayUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]]];

Sort based on the value of the integer.

Upvotes: 16

jscs
jscs

Reputation: 64002

You need your strings to be compared with the NSNumericSearch option:

NSNumericSearch
Numbers within strings are compared using numeric value, that is, Name2.txt < Name7.txt < Name25.txt.

which requires the compare:options: method to be called for the comparison.

In order to do that, your sort descriptor can use an NSComparator Block:

[NSSortDescriptor sortDescriptorWithKey:@"self"
                              ascending:YES
                             comparator:^(NSString * string1, NSString * string2){
                                            return [string1 compare:string2
                                                            options:NSNumericSearch];
 }];

Or, indeed, you can skip the sort descriptor and simply sort the array directly, using the same Block:

[unsortedList sortedArrayUsingComparator:^NSComparisonResult (NSString * string1, NSString * string2){
    return [string1 compare:string2
                    options:NSNumericSearch];
 }];

Upvotes: 11

Vincent Gable
Vincent Gable

Reputation: 3463

[list sortUsingSelector:@selector(localizedStandardCompare:)]; will sort the list in a "human" way (so "11" will come last, not between "1" and "2"). But if you really do want to treat these strings as numbers, you should make them number first!

Upvotes: 39

jonkroll
jonkroll

Reputation: 15722

You can do this by implementing a custom comparator block when creating your NSSortDescriptor:

NSSortDescriptor *aSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sort" ascending:YES comparator:^(id obj1, id obj2) {

    if ([obj1 integerValue] > [obj2 integerValue]) {
        return (NSComparisonResult)NSOrderedDescending;
    }
    if ([obj1 integerValue] < [obj2 integerValue]) {
        return (NSComparisonResult)NSOrderedAscending;
    }
    return (NSComparisonResult)NSOrderedSame;
}];
self.myList = [NSMutableArray arrayWithArray:[unsortedList sortedArrayUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]]];

See Apple documentation here

Upvotes: 44

Related Questions