Sort NSArray of custom objects by their NSDate properties - objective-c

I am attempting to sort an NSArray that is populated with custom objects. Each object has a property startDateTime that is of type NSDate.
The following code results in an array, sortedEventArray, populated but not sorted. Am I going about this the completely wrong way or am I just missing something small?
NSSortDescriptor *dateDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"startDateTime"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:dateDescriptor];
NSArray *sortedEventArray = [nodeEventArray sortedArrayUsingDescriptors:sortDescriptors];

Are you sure that the startDateTime instance variables of the node events are non-nil?
If you don't have one already, you might add a (custom) -description method to your node event objects that does something like this:
- (NSString *)description {
return [NSString stringWithFormat:#"%# - %#",
[super description], startDateTime]];
}
Then in your sorting code log the array before and after:
NSLog(#"nodeEventArray == %#", nodeEventArray);
NSSortDescriptor *dateDescriptor = [NSSortDescriptor
sortDescriptorWithKey:#"startDateTime"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:dateDescriptor];
NSArray *sortedEventArray = [nodeEventArray
sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"sortedEventArray == %#", sortedEventArray);
If the startDateTime's are all nil, then the before and after arrays will have the same order (since the sorting operation will equate to sending all the -compare: messages to nil, which basically does nothing).

Did you try by specifying the NSDate comparator?
Eg:
NSSortDescriptor *dateDescriptor = [NSSortDescriptor
sortDescriptorWithKey:#"startDateTime"
ascending:YES
selector:#selector(compare:)];
This should enforce the usage of the correct comparator of the NSDate class.

Ok, I know this is a little late, but this is how I would do it:
NSArray *sortedEventArray = [events sortedArrayUsingComparator:^NSComparisonResult(Event *event1, Event *event2) {
return [event1.startDateTime compare:event2.startDateTime];
}];
Obviously, replace Event with the class of your custom object. The advantage of this approach is that you are protected against future refactoring. If you were to refactor and rename the startDateTime property to something else, Xcode probably would not change the string you're passing into the sort descriptor. Suddenly, your sorting code would break/do nothing.

You can achieve like this,
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"anyDateField" ascending:YES];
NSMutableArray *arr = [[array sortedArrayUsingDescriptors:[NSArray arrayWithObjects:descriptor,nil]]mutableCopy];

You can achieve this like this,
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:FALSE];
[self.Array sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

Related

Ios error while sorting an NSArray

When I try to sort an array, I got an exception like below:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key levels.'
To sort the arrays I use the following lines:
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"levels" ascending:YES];
NSArray *descriptors = [NSArray arrayWithObject: descriptor];
NSArray *reversedLevelIdArray = [levelsDictionary.allKeys sortedArrayUsingDescriptors:descriptors];
The levelsDictionary.allKeys contains the values like below:
( level2, level1 )
What is wrong with my code?
Using Sort Descriptor...
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"self" ascending:YES];
NSArray *descriptors = [NSArray arrayWithObject: descriptor];
NSArray *reversedLevelIdArray = [levelsDictionary.allKeys sortedArrayUsingDescriptors:descriptors];
NSLog(#"%#", reversedLevelIdArray);
You can use #selector to sort an array...
NSArray *resultArray = [levelsDictionary.allKeys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
Using Comparator....
NSArray *resultArray = [levelsDictionary.allKeys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return obj2 > obj1;
}];
Just use the following codes to sort the array, instead of all of your other line of codes..
NSArray *reversedIdArray = [levelsDictionary.allKeys sortedArrayUsingDescriptors:#selector(caseInsensitiveCompare:)];
You're sorting strings and they are ahving no property or method named levels. You should sort the values instead, like:
[levelsDictionary.allKeys sortedArrayUsingSelector:#selector(compare:)];
I have checked you code, you are sorting the data and accessing the wrong key name of dictionary.
As you said that your levelDictionary contains the keys name as "level2", "level1", etc....
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"levels" ascending:YES];
In this code you have used the key name as "levels" is wrong, because I think your dictionary "levelsDictionary" does not contains any key name as "levels".
Please try to use the proper key name and your error will resolve.

Sort descriptor not working on nsdate objects in NSArray.

An array contains NSDate objects, how to sort these using sort descriptor ? I am trying the code below:
NSSortDescriptor * dateSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"Date" ascending:YES];
NSArray * sortedArray = [allDates addObjectsFromArray:[allDates sortedArrayUsingDescriptors:#[dateSortDescriptor]]];
NSDate conforms to the compare: method and ascending is the default order
NSArray *sortedArray = [allDates sortedArrayUsingSelector:#selector(compare:)];
#Avt, answered this question I don't know why he removed it.
NSSortDescriptor * dateSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"timeIntervalSince1970" ascending:YES];
NSArray * sortedArray = [allDates addObjectsFromArray:[allDates sortedArrayUsingDescriptors:#[dateSortDescriptor]]];

Ordering an NSArray with NSDictionaries with objects

I have an array of dictionaries that contains objects like in the image below:
The problem I have is that I need to order the array by dates, for example in the first object I have a dueDate that is greater than the dueDate of the second object, then I have to swap them, so in other word I need to order the dictionaries inside the array based on the dueDate in ascending order, anyone have ideas?, I have tried with NSpredicate, with sortDescriptors but it isnt working, here is my sortDescriptors code:
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"dueDate" ascending:YES];
NSSortDescriptor *descriptor2 = [[NSSortDescriptor alloc] initWithKey:#"overDueDate" ascending:YES];
NSArray *descriptors = #[descriptor, descriptor2];
tasksResultArray = [tasksResultArray sortedArrayUsingDescriptors:descriptors];
Thanks in advance.
It can be done easier:
arrayOfDictionaries = [arrayOfDictionaries sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
// The logic for comparing dates, and returning the corresponding result
NSDate *date1 = (NSDate *) [(NSDictionary *)obj1 objectForKey:#"dueDate"];
NSDate *date2 = (NSDate *) [(NSDictionary *)obj2 objectForKey:#"dueDate"];
return [date1 compare:date2];
}];

Function in a Sort Descriptor

In my core data model, I have a person entity with properties of firstName and lastName. When I use sort descriptors with a fetchedResultsController, I sort by firstName. But if the firstName is nil, I want to use the lastName instead.
I also have a relationship between person and objects, called personOfObjects where person<-->>objects. I am sorting to present in a UITableView. It works fine except for the cases where the first name is nil. In that case they all get grouped into the same group, when these special cases should really be sorted by the last name instead.
Here is what I do now:
NSEntityDescription * entity = [NSEntityDescription entityForName:#"object" inManagedObjectContext:dataInterface.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *personSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"personOfObject.firstName" ascending:YES];
NSSortDescriptor *objectSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"objectDescription" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:personSortDescriptor, objectSortDescriptor, nil];
I thought I could create a helper function in the person class, like this:
- (NSString *) firstLast {
if (![self.firstName isNullString] ) return self.firstName;
if (![self.lastName isNullString] ) return self.lastName;
return #"";
}
and then change the first sort descriptor to refer to this function:
NSSortDescriptor *personSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"personOfObject.firstLast" ascending:YES];
NSSortDescriptor *objectSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"objectDescription" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:personSortDescriptor, objectSortDescriptor, nil];
I didn't really expect this to work (and it didn't), but I am wondering if there is a straightforward way to do something like this.
You can do this by using your own NSComparator ( How to use NSComparator? )
this question has some code for it, too:
How to Sort an NSMutableArray of Managed Objects through an object graph
You'll have to add your conditional compare in the comparator rather than chaining sort descriptors.

How to sort an array which contains Dictionaries?

I have an array which contains dictionaries in it; so how can I sort the array according to dictionary key values?
If every element in the array is a dictionary containing a certain key, you can sort the array using a sort descriptor. For instance, if every element is a dictionary containing a key called "name":
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:#"name"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:sortDescriptors];
The other way to achieve this would be using sortedArrayUsingComparator: method
NSArray *sortedArray = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [[obj1 valueForKey:#"value"] compare:[obj2 valueForKey:#"value"]];
}];
why don't you use a sorted dictionary where the property of sorted elements comes already with the data structure?
Please do check it out here
Hope this helps.
The above shared answer by Bavarious helped me.Just want to add one more thing.If you want to sort a mutable array use something like the below code
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
NSMutableArray *sortedArray = [[NSMutableArray sortedArrayUsingDescriptors:sortDescriptors]mutableCopy];
let arrDescriptor = NSSortDescriptor(key: "order", ascending: true)
let sortDescriptors: NSArray = [arrDescriptor]
let sortedArray = instancesubArray.sortedArray(using: sortDescriptors as [AnyObject] as [AnyObject] as! [NSSortDescriptor])
print(sortedArray)