Search code examples
objective-csortingnsmutablearraynsdecimalnumbernstablecolumn

NSTableColumn sorts numbers wrongly


This is a NSTableView with IB bindings to a NSArrayController, it displays all values correctly.
However it sorts the numbers only by their first char value e.g. it will put 115.31 below 2.5, and 23.9 below 4.71, etc.

It takes values from a retained NSMutableArray with Strings in it, I also tried by converting all strings to NSNumber/NSDecimalNumber, still no luck:

NSMutableArray *array1 = [[string1 componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]] mutableCopy];

NSMutableArray *array1alt = [NSMutableArray array];

for(NSString *strNum in array1)
{
    NSNumber *number = strNum;
    [array1alt addObject:number];
}

Please help, thanks.

EDIT: This is how NSMutableArray(s) of my NSTableColumn(s) get filled:

NSMutableArray *rows = [NSMutableArray array];

for (NSUInteger i = 0; i < array1alt.count && i < array2.count && i < array3.count && i < array4.count; i++)
{
    NSMutableDictionary *row = [NSMutableDictionary dictionary];
    [row setObject:[array1alt objectAtIndex:i] forKey:@"Apples"];
    [row setObject:[array2 objectAtIndex:i] forKey:@"Oranges"];
    [row setObject:[array3 objectAtIndex:i] forKey:@"Peaches"];
    [row setObject:[array4 objectAtIndex:i] forKey:@"Plums"];
    [rows addObject:row];
}

[myArrayController2 setContent:rows2];
[aTableView reloadData];

Solution

  • I'm surprised that you aren't getting a compiler warning at:

    NSNumber *number = strNum;
    

    You probably want:

    NSNumber *number = [NSNumber numberWithDouble:[strNum doubleValue]];
    

    Or, more simply:

    NSNumber *number = @([strNum doubleValue]);
    

    If you don't want to deal with number conversions on the output, you could sort your original array of strings like so:

    NSArray *array2 = [array1 sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
        double num1 = [obj1 doubleValue];
        double num2 = [obj2 doubleValue];
        if (num1 < num2)
            return NSOrderedAscending;
        else if (num1 > num2)
            return NSOrderedDescending;
        else
            return NSOrderedSame;
    }];
    

    If you want to use decimal numbers, you could probably do something like:

    NSMutableArray *array2 = [NSMutableArray array];
    for (NSString *strNum in array1)
    {
        [array2 addObject:[NSDecimalNumber decimalNumberWithString:strNum]];
    }
    
    [array2 sortUsingComparator:^NSComparisonResult(NSDecimalNumber *obj1, NSDecimalNumber *obj2) {
        return [obj1 compare:obj2];
    }];