-3

I want to sort an array of NSStrings by length, but then have all strings that are the same length be sub-sorted alphabetically. For example, "cat, hat, zen, nine, rate, tale, access, vanish."

How can I do this? I've been trying to figure out NSSortDescriptors for like an hour and gotten nowhere. I do not know what keys to use in order to sort NSStrings alphabetically or by length. I have sorted them alphabetically using sortedArrayUsingSelector, but over KevinTTrimm's comment's link, I need to use NSSortDescriptor in order to sort by two metrics, so I need to know the keys.

temporary_user_name
  • 35,956
  • 47
  • 141
  • 220
  • 2
    Surely you must have accomplished something in that hour. It would help us if you could show us how far you got and where you got stuck. – David Rönnqvist Nov 24 '14 at 21:10
  • possible duplicate of [How to establish secondary NSSortDescriptor sort key?](http://stackoverflow.com/questions/8410637/how-to-establish-secondary-nssortdescriptor-sort-key) – KevinDTimm Nov 24 '14 at 21:14
  • @DavidRönnqvist I'm stuck at knowing what properties (keys) to use with NSSortDescriptor in order to sort strings alphabetically or by length. I saw the answer KevenDTrimm linked to but all examples I've found only show you how to do it with arbitrary objects. I haven't figured out how to sort NSStrings using the NSSortDescriptors because I don't know what properties to use. And now this is just getting downvoted into oblivion for no real reason, so it's pretty discouraging. God forbid experienced iOS programmers consider this trivial-- automatically a stupid question, DOWNVOTE. – temporary_user_name Nov 24 '14 at 21:19
  • 2 down votes is not oblivion ;) Note too that if you follow the advice of the first comment and some of the questions in the sidebar you'll probably get farther than you have. Remember that down votes can be rescinded. – KevinDTimm Nov 24 '14 at 21:22
  • What key do you use with NSSortDescriptor to sort strings alphabetically!!!!!!!!!!! ***Without*** using `sortedArrayUsingSelector`!! – temporary_user_name Nov 24 '14 at 21:32
  • However over the down arrow and you will see the first reason for a downvote "This question does not show any research effort". To someone who reads your question it doesn't matter the effort you've spent trying to solve it yourself if you can't show that in the question. This is not just to be mean. Explaining what you have the problem and what you have done forces you to reflect on it and can [be part of solving the problem yourself](http://en.wikipedia.org/wiki/Rubber_duck_debugging). – David Rönnqvist Nov 24 '14 at 21:32
  • Eet hast bein edited. – temporary_user_name Nov 24 '14 at 21:35

3 Answers3

5

tl;dr: the key paths you are looking for are "length" and "self" (or "description" or "uppercaseString" depending on what how you want to compare)


As you can see in the documentation for NSSSortDescriptor, a sort descriptor is created "by specifying the key path of the property to be compared". When sorting string by their length, the property on NSString that you are looking for is length:

@property(readonly) NSUInteger length;

Since you want the words to be longer and longer, that sort descriptor should be ascending so that later values have a greater length than the previous value.

NSSortDescriptor *byLength =
    [NSSortDescriptor sortDescriptorWithKey:@"length" ascending:YES];

Next, to sort the string alphabetically, what you really want is to compare the string directly with the other string. There are a couple of different ways you can do this. For example, the description method on NSString (which can be invoked using KVC) is documented to return "This NSString object", meaning the object itself. That would be one option.

NSSortDescriptor *alphabeticallyAlternative1 =
    [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES];

Other methods you could invoke over KVC are for example uppercaseString or lowercaseString. Using either of these in the sort descriptor will make it an case-insensitive comparison (which either is useful to you, or it isn't).

// case-insensitive comparison
NSSortDescriptor *alphabeticallyAlternative2 =
    [NSSortDescriptor sortDescriptorWithKey:@"uppercaseString" ascending:YES];

A third way of comparing the string itself directly with the other string is to use the key path "self". You will sometimes see this being used in predicates.

NSSortDescriptor *alphabeticallyAlternative3 =
    [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES];

In all 3 cases the sort descriptor was made ascending so that letters that appear later in the alphabet come later in the array.


Whichever version of alphabetical sorting that you pick, since you want to sort by length first you would pass the byLength descriptor first and the alphabetical descriptor second.

NSArray *sorted =
    [someStrings sortedArrayUsingDescriptors:@[byLength, alphabetically]];
David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
2

I've been trying to figure out NSSortDescriptors for like an hour

Why use NSSortDescriptors at all? I mean, they are nice, but this is so much easier using something like sortedArrayUsingComparator: because you can just write your own test saying what counts as greater than and what counts as less than.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • And here's an example, from my book: http://www.apeth.com/iOSBook/ch03.html#_blocks – matt Nov 24 '14 at 21:41
  • MY GOD, A USEFUL ANSWER! There is hope left. I'm trying to find a clear explanation of how `compare` works...I need to adjust the block to sort by length and then alphabetical if lengths are equal. I mean, I'm willing to go with any approach that works-- it's just a matter of which one I manage to understand enough to correctly modify first. – temporary_user_name Nov 24 '14 at 21:45
  • I edited it to this-- `NSArray* arr2 = [words sortedArrayUsingComparator: ^(NSString* s1, NSString* s2) { if([s1 length] != [s2 length]){ return [s1 compareBySize:s2]; } else return [s1 compare:s2]; }];` but `compareBySize` of course isn't a real selector so I'm trying to figure out how that should actually be written. Would be easy if I could just use boolean values, but of *course* there has to be this NSComparisonResult thing.... – temporary_user_name Nov 24 '14 at 21:54
-1

Finally got it with:

NSArray* words2 = [words sortedArrayUsingComparator: ^(NSString* s1, NSString* s2) {
    if([s1 length] != [s2 length]){
        NSNumber* s1length = [NSNumber numberWithInt:[s1 length]];
        NSNumber* s2length = [NSNumber numberWithInt:[s2 length]];
        return [s1length compare:s2length];
    }
    else return [s1 compare:s2];
}];

Not sure why they felt boolean was insufficient and added NSComparisonResult, but whatever...

temporary_user_name
  • 35,956
  • 47
  • 141
  • 220