1

All,

I would like to sort this mutable dictionary of arrays keys like so: A-Z 0-9 but it's coming back sorted 0-9 A-Z. How can I sort it such that the alpha chars come before the numbers? Perhaps there is a built-in method that does this or should I create a category that extends NSString?

    NSArray *sKeysArray = [[listContent allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
    NSString *sectionKey = [sKeysArray objectAtIndex:section];
    NSArray *sectionValues = [listContent valueForKey:sectionKey];
Monolo
  • 18,205
  • 17
  • 69
  • 103
Slinky
  • 5,662
  • 14
  • 76
  • 130

2 Answers2

4

Sadly there's no method in NSString that will directly do this. Instead of writing a category, I would just use NSArrays sortedArrayUsingComparator: method:

NSArray *sKeysArray = [[listContent ALlKeys] sortedArrayUsingComparator:^(id obj1, id obj2) {
// Code to compare obj1 and obj2 and return an NSComparisonResult here; return NSOrderedAscending if obj1 should come before obj2 
}];

To do each comparison, I would use NSString's – enumerateSubstringsInRange:options:usingBlock: passing NSStringEnumerationByComposedCharacterSequences for options (basically enumerating characters, except that unicode characters sequences which are actually one "letter" are combined). And when you're comparing two characters, use something like the answer to this question to detect when obj1 is a number and obj2 is regular letter and return NSOrderedDescending, otherwise just use regular compare:.

Community
  • 1
  • 1
yuji
  • 16,695
  • 4
  • 63
  • 64
2

Like @yuri says, sortedArrayUsingComparator: should provide the basic functionality.

Here is a simple version that sorts strings beginning with a decimal digit after strings beginning with an alphabetic character (in NSCharacterSet's definition), and leaves all other combinations of strings to their default sort order:

NSArray *arr = [NSArray arrayWithObjects:
                 @"ABC 09 GHJ", @"DEF23PO", @"123ABC", @"MCMLXV", @"[cocoa]", @"$2000", @"ERT", @"24seven", @"zero",
                @"Ångström", @"Übernacht",
                nil];

NSCharacterSet *digits    = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *alphanums = [NSCharacterSet alphanumericCharacterSet];

NSArray *sorted = [arr sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {

    if ( [obj1 isKindOfClass:[NSString class]] && [obj1 length] > 0
        && [obj2 isKindOfClass:[NSString class]] && [obj2 length] > 0 ) {

        unichar c1 = [obj1 characterAtIndex:0];
        unichar c2 = [obj2 characterAtIndex:0];

        if ( [digits characterIsMember:c1] && (![digits characterIsMember:c2] && [alphanums characterIsMember:c2]) ) {
            return NSOrderedDescending;
        }

        if ( [digits characterIsMember:c2] && (![digits characterIsMember:c1] && [alphanums characterIsMember:c1]) ) {
            return NSOrderedAscending;
        }
    }

    return [obj1 compare: obj2];
}];

NSLog(@"Sorted: %@", sorted);

Caveat: It will not handle a unicode letter composed of a decimal digit as the first component correctly, but I will consider that a pathological case until someone can enlighten me.

Monolo
  • 18,205
  • 17
  • 69
  • 103