Reputation: 16428
I have the following sort descriptors that sort an array of my business objects, ready to be displayed in a table, I'm starting off with some sample sorting code from a previous SO question
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"awardedOn" ascending:NO];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor1, sortDescriptor2, nil];
NSArray *sortedArray = [returnable sortedArrayUsingDescriptors:sortDescriptors];
The objects that I'm displaying all will have a title. Only some of them will have an "awardedOn" set, which is an NSDate.
What I want to do:
Something like this (Titles, the bold ones have a value for awardedOn)
Upvotes: 4
Views: 4412
Reputation: 156
You should be able to do that using two descriptors like you first said, first by awardedOn, then by title. However, you need to provide a custom NSSortDescriptor for the awardedOn sort that looks someting like this:
#define NULL_OBJECT(a) ((a) == nil || [(a) isEqual:[NSNull null]])
@interface AwardedOnSortDescriptor : NSSortDescriptor {}
@end
@implementation AwardedOnSortDescriptor
- (id)copyWithZone:(NSZone*)zone
{
return [[[self class] alloc] initWithKey:[self key] ascending:[self ascending] selector:[self selector]];
}
- (NSComparisonResult)compareObject:(id)object1 toObject:(id)object2
{
if (NULL_OBJECT([object1 valueForKeyPath:[self key]])) {
if (NULL_OBJECT([object2 valueForKeyPath:[self key]]))
return NSOrderedSame; // If both objects have no awardedOn field, they are in the same "set"
return NSOrderedDescending; // If the first one has no awardedOn, it is sorted after
}
if (NULL_OBJECT([object2 valueForKeyPath:[self key]])) {
return NSOrderedAscending; // If the second one has no awardedOn, it is sorted after
}
return NSOrderedSame; // If they both have an awardedOn field, they are in the same "set"
}
@end
This will allow you to have to separate sets: Awesome/Better/Cool and Another/Another One/One More/Yet another, in your example. After that, you should be good with:
NSSortDescriptor *sortDescriptor1 = [[AwardedOnSortDescriptor alloc] initWithKey:@"awardedOn" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];
On a final note, you might need a litte more work depending on what your "empty" awardedOn fields look like (I assumed, in the code above, that the field was set to null). You can take a look here: https://stackoverflow.com/a/3145789
Upvotes: 1
Reputation: 160
Try to use comparator:
_finalArray = [_array sortedArrayUsingComparator: ^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2)
{
if([[obj1 valueForKey@"awardedOn"] lenght] && ![[obj2 valueForKey@"awardedOn"] lenght])
return NSOrderedDescending;
if([[obj2 valueForKey@"awardedOn"] lenght] && ![[obj1 valueForKey@"awardedOn"] lenght])
return NSOrderedAscending;
return NSOrderedSame;
}];
Upvotes: 0
Reputation: 49354
There are two possible ways:
When creating a business object, assign awardedOn
date as distantPast
if it does not exist and then do normal sorting by awardedOn
and then by title
.
Create sort descriptor with custom comparison method that will be called on each of business objects:
.
NSSortDescriptor *awardDescriptor = [NSSortDescriptor descriptorWithKey:@"awardedOn"
ascending:NO
selector:@selector(compareAwardedOn:)];
// In class for business object
- (NSComparisonResult)compareAwardedOn:(id)otherBusiness {
// return custom NSComparison result after
// checking whether either of awardedOn dates are nil.
return NSOrderedSame;
}
Upvotes: 1