views:

1322

answers:

3

Hi,

I have loaded item from core data in an NSMutableArray. Each item, when created, is given a due date, that the user choices.

How do I sort, so only the item that is due today is displayed?

Here is what I got so far:

NSPredicate *predicate = [NSPredicate predicateWithFormat: @"dueDate == %@", [NSDate date]];

[allObjectsArray filterUsingPredicate: predicate];

This code doesn't work however.

Thanks for any suggestions

A: 

The method -filterUsingPredicate: only works on mutable arrays (of type NSMutableArray).

Try using the -filteredArrayUsingPredicate: method, instead:

NSString *formattedPredicateString = [NSString stringWithFormat:@"dueDate == '%@'", [NSDate date]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:formattedPredicateString];
NSArray *filteredArray = [allObjectsArray filteredArrayUsingPredicate:predicate];
Alex Reynolds
A: 

The problem with using predicates is that if they use standard date comparison, it'll only return dates that are exactly the date and time of the given date. If you want "today" dates, you'll need to add a -isToday method somewhere (possible as an extension to NSDate), like this:

-(BOOL)dateIsToday:(NSDate *)aDate {

    NSDate *now = [NSDate date];

    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *nowComponents = [cal components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit 
           fromDate:now];

    NSDateComponents *dateComponents = [cal components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit 
            fromDate:aDate];

    return (([nowComponents day] == [dateComponents day]) &&
  ([nowComponents month] == [dateComponents month]) && 
  ([nowComponents year] == [dateComponents year]));

}

Once you have that, it's simple enough to find the ones that are today:

NSMutableArray *itemsDueToday = [NSMutableArray array];

for (MyItem *item in items) {
    if ([self dateIsToday:[item date]) {
        [itemsDueToday addObject:item];
    }
}

// Done!
iKenndac
This approach would work, however it requires a lot of date processing and calculation per item. So if there were a few hundred items (or more) then the processing time would start to add up.
Michael Waterfall
And on top of that there is an extra cost to be aware of:http://www.mikeabdullah.net/NSCalendar_currentCalendar.html
Mike Abdullah
+1  A: 

How about you just calculate today at 00:00 and then tomorrow at 00:00 and then compare the dates in the predicate to those (>= and <). So all date objects must be within those two dates to be classed as being 'Today'. This requires you to only calculate 2 dates initially, no matter how many date objects are in your array.

// Setup
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];

// Get todays year month and day, ignoring the time
NSDateComponents *comp = [cal components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:now];

// Components to add 1 day
NSDateComponents *oneDay = [[NSDateComponents alloc] init];
oneDay.day = 1;

// From & To
NSData *fromDate = [cal dateFromComponents:comp]; // Today at midnight
NSData *toDate = [cal dateByAddingComponents:oneDay toDate:fromDate options:0]; // Tomorrow at midnight

// Cleanup
[oneDay release]

// Filter Mutable Array to Today
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"dueDate >= %@ && dueDate < %@", fromDate, toDate];
NSArray *filteredArray = [allObjectsArray filteredArrayUsingPredicate:predicate];

// Job Done!
Michael Waterfall
+1 this is probably your best option. An alternative to `>= ` (Notice that you don't have to construct a `predicateString` object when using `predicateWithFormat:`)
Dave DeLong
Thanks for the +1! Yes I thought about using the `BETWEEN` keyword, however as the toDate is the next day at midnight then I wanted the predicate to be less than but not equal to that date. If I remember correctly the `BETWEEN` is inclusive of the upper and lower bounds. Also you're quite right about the `predicateWithFormat:`!
Michael Waterfall