I'm using NSSortDescriptor
to sort an NSArray
of NSDictionary
items. Works great, just what I needed... except that I'd like for the dictionary items that have blank values for my sort key to show up at the end of the sorted list. Is there a way to easily accomplish this? Or will I have to create some custom sorting functions? And no, I don't want to just set it to DESC order... I'd like sorted results to look like A, A, B, B, C, blanks, blank.
-
What do you mean by blank values? No value, empty string, whitespace or `NSNull`? – Costique Mar 16 '12 at 20:26
-
I meant empty string. Loading the NSDictionary from a plist, where the value comes from:
. – Bryan Mar 16 '12 at 21:05
3 Answers
Figured this out after seeing another example using a custom comparison. Here is the code I ended up with:
@interface NSString (CustomStatusCompare)
- (NSComparisonResult)customStatusCompare:(NSString*)other;
@end
@implementation NSString (CustomStatusCompare)
- (NSComparisonResult)customStatusCompare:(NSString*)other {
NSAssert([other isKindOfClass:[NSString class]], @"Must be a NSString");
if ([self isEqual:other]) {
return NSOrderedSame;
}
else if ([self length] > 0 && [other length] > 0) {
return [self localizedCaseInsensitiveCompare:other];
}
else if ([self length] > 0 && [other length] == 0) {
return NSOrderedAscending;
}
else {
return NSOrderedDescending;
}
}
@end
NSSortDescriptor *serviceTypeDescriptor =
[[NSSortDescriptor alloc] initWithKey:@"Service"
ascending:YES
selector:@selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *locationDescriptor =
[[NSSortDescriptor alloc] initWithKey:@"Location"
ascending:YES
selector:@selector(customStatusCompare:)]; //using custom comparison here!
NSArray *descriptors = [NSArray arrayWithObjects:locationDescriptor, nameDescriptor, nil];
self.navArray = [self.navArray sortedArrayUsingDescriptors:descriptors];
So the comparer returns NSOrderedSame if both strings are empty... calls the regular comparison function if both strings are non-empty... if only one string is empty, it reverses the normal order of that comparison. Voila!

- 8,748
- 7
- 41
- 62
-
Wow, sorry, I somehow totally misread your question and thought you wanted strings with leading blanks first :/ I'll go work on my reading comprehension... – Doug McBride Mar 19 '12 at 20:08
-
Note that this won't work within a compile predicate in core data :/ – deepwinter Apr 11 '18 at 22:49
Do the Sort using NSNumericSort instead of NSDiacriticInsensitive or NSCaseInsensitive. Since the whitespace is probably 256 in ascii code it will be sent to the back of the list.

- 1,539
- 1
- 14
- 28
Edit: I misread the question and thought the intent was to put strings with leading spaces first in sort order (which they should anyway).
You want something like this, I think. This is completely untested and doesn't take multiple leading spaces into account. I'm sure there's a more elegant way, but it should get the job done.
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {
NSString *str1 = (NSString *)obj1;
NSString *str2 = (NSString *)obj2;
BOOL str1HasSpace = [str1 hasPrefix:@" "];
BOOL str2HasSpace = [str2 hasPrefix:@" "];
if (str1HasSpace && !str2HasSpace) {
return NSOrderedAscending;
} else if (str2HasSpace && ! str1HasSpace) {
return NSOrderedDescending;
} else {
return [[str1 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] compare:[str2 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
}
}];

- 514
- 1
- 4
- 13
-
Thanks, I see where you're going here. What I ended up doing was very similar... putting this custom comparison inside its own method in an NSString category (is that the right name?) instead of a block. – Bryan Mar 16 '12 at 20:55