问题
I have an NSArrayController
and I would like to sort the contents so that anything with English alphabets are sorted first and then anything with numbers and non English characters are sorted last.
For example: A, B , C ... Z, 1 , 2, 3 ... 9, 구, 결, ...
Currently I only know how to sort items in alphabetical order. Suggestions?
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
[dataController setSortDescriptors: [NSArray arrayWithObject: sort]];
回答1:
You can use sortedArrayUsingComparator
to customize the sort algorithm to your needs. For instance, you can give precedence to symbols with this lines:
NSArray *assorted = [@"1 2 3 9 ; : 구 , 결 A B C Z ! á" componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSArray *sorted = [assorted sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
/* NSOrderedAscending, NSOrderedSame, NSOrderedDescending */
BOOL isPunct1 = [[NSCharacterSet punctuationCharacterSet] characterIsMember:[(NSString*)obj1 characterAtIndex:0]];
BOOL isPunct2 = [[NSCharacterSet punctuationCharacterSet] characterIsMember:[(NSString*)obj2 characterAtIndex:0]];
if (isPunct1 && !isPunct2) {
return NSOrderedAscending;
} else if (!isPunct1 && isPunct2) {
return NSOrderedDescending;
}
return [(NSString*)obj1 compare:obj2 options:NSDiacriticInsensitiveSearch|NSCaseInsensitiveSearch];
}];
To put English characters before non-English ones, it'd be enough to use NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
as options, no fancy algorithm required.
If you need to support iOS without blocks try sortedArrayUsingSelector
.
回答2:
Another solution by testing, if a string is latin1-encodeable:
- test for both strings, if the first character is a latin (aka english) character
- if both are, test if both starts either with a letter or a number. In both cases, leave it up the
compare:
else, if one starts with the number and one with a letter,
return NSOrderingAscending
, if the one with letter its first, otherwiseNSOrderingDescending
If both strings aren't latin, let
compare:
decide again- if one is latin, and one not,
return NSOrderingAscending
if the latin is first, otherwiseNSOrderingDescending
the code
NSArray *array = [NSArray arrayWithObjects:@"peach",@"apple",@"7",@"banana",@"ananas",@"5", @"papaya",@"4",@"구",@"결",@"1" ,nil];
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString *s1 = [obj1 substringToIndex:1];
NSString *s2 = [obj2 substringToIndex:1];
BOOL b1 = [s1 canBeConvertedToEncoding:NSISOLatin1StringEncoding];
BOOL b2 = [s2 canBeConvertedToEncoding:NSISOLatin1StringEncoding];
if ((b1 == b2) && b1) {//both number or latin char
NSRange r1 = [s1 rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]];
NSRange r2 = [s2 rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]];
if (r1.location == r2.location ) { // either both start with a number or both with a letter
return [obj1 compare:obj2 options:NSDiacriticInsensitiveSearch|NSCaseInsensitiveSearch];
} else { // one starts wit a letter, the other with a number
if ([s1 rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]].location == NSNotFound) {
return NSOrderedAscending;
}
return NSOrderedDescending;
}
} else if((b1 == b2) && !b1){ // neither latin char
return [obj1 compare:obj2 options:NSDiacriticInsensitiveSearch|NSCaseInsensitiveSearch];
} else { //one is latin char, other not
if (b1) return NSOrderedAscending;
return NSOrderedDescending;
}
}];
for (NSString *s in array) NSLog(@"%@", s);
result
ananas
apple
banana
papaya
peach
1
4
5
7
구
결
回答3:
I don't think you can do that kind of sorting without defining your own comparison function.
To this aim, you could use sortedArrayUsingFunction:
[array sortedArrayUsingFunction:f context:userContext];
where f is defined as:
NSInteger f(id num1, id num2, void *context)
{
int v1 = [num1 intValue];
int v2 = [num2 intValue];
if (...)
return NSOrderedAscending;
else if (...)
return NSOrderedDescending;
else
return NSOrderedSame;
}
If you prefer not creating function for doing this you could use the block-version of the method, sortedArrayUsingComparator:
[array sortedArrayUsingComparator: ^(id obj1, id obj2) {
return NSOrderedSame;
}];
回答4:
A sort descriptor based on a comparator should do the trick (note: not tested).
NSComparator cmp = ^(id str1, id str2) {
// Make your sorting
if ( /* str1 before str2 */ )
return NSOrderedAscending
else if ( /* str2 after str1 */ )
return NSOrderedDescending
else
return NSOrderedSame
};
NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey: sortKey ascending: YES comparator: cmp];
NSArrayController *ac = // ...
[ac setSortDescriptor: sd];
You of course have to define your own sort order algorithm - but this example should show how to use a sort descriptor for an array controller.
回答5:
One thing is missing to answer properly to the question : NSNumericSearch
NSArray *assorted = [@"1 2 3 9 ; : 구 , 결 A B C Z ! á" componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSArray *sorted = [assorted sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
/* NSOrderedAscending, NSOrderedSame, NSOrderedDescending */
BOOL isPunct1 = [[NSCharacterSet punctuationCharacterSet] characterIsMember:[(NSString*)obj1 characterAtIndex:0]];
BOOL isPunct2 = [[NSCharacterSet punctuationCharacterSet] characterIsMember:[(NSString*)obj2 characterAtIndex:0]];
if (isPunct1 && !isPunct2) {
return NSOrderedAscending;
} else if (!isPunct1 && isPunct2) {
return NSOrderedDescending;
}
return [(NSString*)obj1 compare:obj2 options:NSDiacriticInsensitiveSearch|NSCaseInsensitiveSearch|NSNumericSearch]|;
}];
来源:https://stackoverflow.com/questions/8242735/how-to-sort-array-controller-alphabetically-with-numbers-last-in-objective-c