Sort NSArray of NSStrings like Addressbook on iphone sort
Asked Answered
P

2

9

I have an array of strings (names)

and i would like to sort them like how the address book on the iphone sorts them

  • eg: éli -> under E
  • eg: àli -> under A
  • eg: 4li -> under #

any suggestions?

Peloria answered 27/7, 2011 at 13:48 Comment(0)
C
11

You need to perform a diacritic insensitive comparison against the strings. NSString has a compare:options: method with an option of NSDiacriticInsensitiveSearch.

NSArray *array = [NSArray arrayWithObjects:@"éli", @"bob", @"earl", @"allen", @"àli", @"aaron", nil];

NSArray *sorted = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return [(NSString*)obj1 compare:obj2 options:NSDiacriticInsensitiveSearch|NSCaseInsensitiveSearch];
}];

Edit:

Here is a full example that will section the results based on the first character diacritic insensitive. I put in a dictionary so you would need to keep track of the sorted keys on your own to display properly.

NSArray *array = [NSArray arrayWithObjects:@"éli", @"bob", @"earl", @"allen", @"àli", nil];

NSArray *sorted = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return [(NSString*)obj1 compare:obj2 options:NSDiacriticInsensitiveSearch|NSCaseInsensitiveSearch];
}];

NSMutableDictionary *sectioned = [NSMutableDictionary dictionary];
NSString *firstChar = nil;

for(NSString *str in sorted)
{
    //Ignore empty strings
    if(![str length])continue;

    NSMutableArray *names = nil;

    //Compare the first character using diacritic insensitive search
    if([str compare:firstChar options:NSDiacriticInsensitiveSearch|NSCaseInsensitiveSearch range:NSMakeRange(0, 1)] == NSOrderedSame)
    {
        names = [sectioned objectForKey:firstChar];
    }
    else
    {
        //decomposedStringWithCanonicalMapping is where the magic happens
        //(it removes the accent mark)
        firstChar = [[str decomposedStringWithCanonicalMapping] substringToIndex:1];
        names = [NSMutableArray array];
        [sectioned setObject:names forKey:firstChar];
    }

    [names addObject:str];
}

NSLog(@"sorted: %@", sorted);
//This is sectioned like the address app
NSLog(@"sectioned: %@", sectioned);
Cotter answered 27/7, 2011 at 14:13 Comment(2)
Is it also possible to do this on an array of custom objects with a property called "name"Peloria
Yes inside of the comparison instead of casting obj1 as NSString cast it as your custom object and access .name, same goes in the for loop where I enumerate over sorted, just change NSString *str to your custom type and just do all the comparisons with the name property.Cotter
D
1

all lies within the compare: method you will use as the selector of your sorting method and more importantly with the NSDiacriticInsensitiveSearch option.

[[yourArray] sortedArrayUsingSelector:@selector(compareTheAddressBookWay:)];

Now you need to add the compareTheAddressBookWay: method in a category

@implementation NSString (YourCategory)

- (NSComparisonResult) compareTheAddressBookWay:(NSString*)iString
{
    return [self compare:iString 
                 options:NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch 
                   range:NSRangeFromString(self)
}

@end

!!Important!!:

Using sortedArrayUsingSelector: will be compliant for iOS up to 2.0
while usingsortedArrayUsingComparator: will ONLY work for iOS 4 and above.

Detrition answered 27/7, 2011 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.