Jimmy
Jimmy

Reputation: 16428

Can you use NSSortDescriptor to sort by a value not being null?

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:

  1. Sort entire array so all the objects with an "awardedOn" set are displayed at the top
  2. Within the two "sets", order them alphabetically
  3. I don't care about the actual value of the date, I'm more interested if it exists or not

Something like this (Titles, the bold ones have a value for awardedOn)

Upvotes: 4

Views: 4412

Answers (3)

ameunier
ameunier

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

mas'an
mas'an

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

Eimantas
Eimantas

Reputation: 49354

There are two possible ways:

  1. 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.

  2. 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

Related Questions