87

I have an NSArray that contains date strings (i.e. NSString) like this: "Thu, 21 May 09 19:10:09 -0700"

I need to sort the NSArray by date. I thought about converting the date string to an NSDate object first, but got stuck there on how to sort by the NSDate object.

Thanks.

1
  • Retagged since this is related to the Foundation framework, and not specific to iPhone. Commented Jul 15, 2009 at 21:26

9 Answers 9

115

If I have an NSMutableArray of objects with a field "beginDate" of type NSDate I am using an NSSortDescriptor as below:

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"beginDate" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
Sign up to request clarification or add additional context in comments.

8 Comments

I know a lot of time passed(when previous answer was accepted there could be no NSSortDescriptor yet), but for future use this one should be the proper answer.
This is an awesome solution. solved my problem in a few lines of code.Thanx a ton @vinzenzweber
Really...I was actually shocked this worked on first run! I thought initWithKey was reserved for dictionaries where the key is defined, but all you need to do is reference a property in an array of objects? Let the light shine vinzenzweberskavitchski!!
NSSortDescriptor has been around since OS X 10.3, in late 2003. This is a slightly different scenario, where the date is a field in another object. With even-more-modern API, you might as well use -[NSMutableArray sortUsingComparator:] (10.6+) and pass a block that returns the result of calling -compare: on the two fields. It would probably be faster than calling -valueForKey: on each object. You could even use -sortWithOptions:usingComparator: and pass options to sort concurrently as well.
how does the above NSSortDescriptor handle nil dates? Do we need to tell the desecriptor what to do with them if so, how?
|
112

Store the dates as NSDate objects in an NS(Mutable)Array, then use -[NSArray sortedArrayUsingSelector: or -[NSMutableArray sortUsingSelector:] and pass @selector(compare:) as the parameter. The -[NSDate compare:] method will order dates in ascending order for you. This is simpler than creating an NSSortDescriptor, and much simpler than writing your own comparison function. (NSDate objects know how to compare themselves to each other at least as efficiently as we could hope to accomplish with custom code.)

4 Comments

It's not clear if the original question was asking about sorting an array of dates, or sorting an array of objects that have a date field. This is the easiest approach if it's the former, but if it's the latter, NSSortDescriptor is the way to go.
@smorgan how does NSSortDescriptor handle nil dates?
Thanks for your help, I think we should read more in Apple Docs but it seems so boring, until you use it.
Could you show this in a snippet code as an example? Thanks in advance.
17

You may also use something like the following:

//Sort the array of items by  date
    [self.items sortUsingComparator:^NSComparisonResult(id obj1, id obj2){
        return [obj2.date compare:obj1.date];
    }];

But this does assume that the date is stored as a NSDate rather a NString, which should be no problem to make/do. Preferably, I recommend also storing the data in it's raw format. Makes it easier to manipulate in situations like this.

Comments

10

You can use blocks to sort in place:

sortedDatesArray = [[unsortedDatesArray sortedArrayUsingComparator: ^(id a, id b) {
    NSDate *d1 = [NSDate dateWithString: s1];
    NSDate *d2 = [NSDate dateWithString: s2];
    return [d1 compare: d2];
}];

I suggest you convert all your strings to dates before sorting not to do the conversion more times than there are date items. Any sorting algorithm will give you more string to date conversions than the number of items in the array (sometimes substantially more)

a bit more on blocks sorting: http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html

3 Comments

This is a variant of the same sub-optimal idea presented by @notoop. In any case, converting to NSDate first is definitely the way to go.
He basically just put my answer with @notoop's answer..... quite unnecessary to double post those if you ask me....
This is much slower than parse date strings into array of NSDate and then sort the array of NSDate, cos there will be 2 * (n - 1) times extra parsing of string. Parsing strings is generally heavy job on CPU.
5

What it worked in my case was the following:

    NSArray *aUnsorted = [dataToDb allKeys];
    NSArray *arrKeys = [aUnsorted sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        NSDateFormatter *df = [[NSDateFormatter alloc] init];
        [df setDateFormat:@"dd-MM-yyyy"];
        NSDate *d1 = [df dateFromString:(NSString*) obj1];
        NSDate *d2 = [df dateFromString:(NSString*) obj2];
        return [d1 compare: d2];
    }];

I had a dictionary, where all keys where dates in format dd-MM-yyyy. And allKeys returns the dictionary keys unsorted, and I wanted to present the data in chronological order.

Comments

5

You can use sortedArrayUsingFunction:context:. Here is a sample:

NSComparisonResult dateSort(NSString *s1, NSString *s2, void *context) {
    NSDate *d1 = [NSDate dateWithString:s1];
    NSDate *d2 = [NSDate dateWithString:s2];
    return [d1 compare:d2];
}

NSArray *sorted = [unsorted sortedArrayUsingFunction:dateSort context:nil];

When using a NSMutableArray, you can use sortArrayUsingFunction:context: instead.

4 Comments

This is a bad idea — a comparison function should be fast, and shouldn't have to create NSDate objects from strings for each comparison. If you're already using -[NSDate compare:], just store the dates in the array and use -sortedArrayUsingSelector: directly.
I think this is a good solution if you are sorting objects that already have an NSDate field though. No?
If the array already contains NSDate objects, you can use my answer above, or even use -[NSMutableArray sortUsingComparator:], which was added in 10.6.
If you are using an array of dictionary in which there is a key-value object for date as NSString along with some other key-value objects, then this solution is the best so far. Just need a bit modification inside the dateSort function. Thumbs up! :)
4

Once you have an NSDate, you can create an NSSortDescriptor with initWithKey:ascending: and then use sortedArrayUsingDescriptors: to do the sorting.

3 Comments

This approach will work, but using -sortedArrayUsingSelector: is a bit simpler and doesn't require creating an extra object.
Pre-editing, the question mentioned "a 'date' key", so I assumed this was really an array of objects/dictionaries that had a date as one field, not an array of raw dates. In that case sortedArrayUsingSelector: is more complex, since it requires writing a custom sort routine to do the lookup.
Yeah, sorry for any confusion — I cleaned up that reference since he was just saying he was storing his array with a 'date' key, and I realized it just made the question more confusing. You're definitely correct about relative complexity given a more complex data structure.
2

Swift 3.0

myMutableArray = myMutableArray.sorted(by: { $0.date.compare($1.date) == ComparisonResult.orderedAscending })

Comments

1

Change this

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"beginDate" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];

To

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"Date" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];

Just change the KEY: it must be Date always

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.