1

I have an NSArray with the following name and date pairs in it. NOTE: Date is always in index 1 of my custom array

MyCustomArray: 
(
"Toms Bday"
"2012-01-10"
)
(
"Jesscia Bday"
"2012-01-27"
)
(
"Jills Bday"
"2012-03-03"
)
(
"Joes Bday"
"2012-04-15"
)

There are hundreds of name date pairs in my array. So for a given StartDate = "2012-01-01" and endDate = "2012-01-31" I want to get just the following records. How do I do that?

(
    "Toms Bday"
    "2012-01-10"
    )
    (
    "Jesscia Bday"
    "2012-01-27"
    )

I looked into this post Why can't NSDate be compared using < or >? but still can't figure it out.

Community
  • 1
  • 1
Sam B
  • 27,273
  • 15
  • 84
  • 121
  • So this is an array of arrays, of which the first item is an `NSString` that represents a date, right? –  Dec 28 '12 at 14:54

4 Answers4

3

Considering your dates have a very simple format, you can compare them as strings and the result should be correct:

NSString *start = @"2012-01-01";
NSString *end = @"2012-01-31";

NSString *date = @"2012-01-10";

if ([start compare:date] == NSOrderedAscending &&
    [end compare:date] == NSOrderedDescending) {
    // Note: might be descending for start and ascending for end, i'm not 100% sure, try both

    // valid date
}
Ismael
  • 3,927
  • 3
  • 15
  • 23
  • 3
    campare is a method of NSDate, so start,end and date must be in NSDate format, Thanks – junaidsidhu Dec 28 '12 at 15:09
  • There's some work to do to convert the strings into dates, and you're not showing how to filter the original array to produce the wanted result. I'd say that is a rather sloppy and incomplete answer, that does not even compile. – Gabriele Petronella Dec 28 '12 at 15:28
  • Thank you so much and thank @JayD for pointing out that I had to convert the dates to NSDate objects. This is a prefect little solution – Sam B Dec 28 '12 at 15:35
  • I'm glad that you find this useful. Still I don't how this could be a good answer since is not giving you the solution to what you asked, which is filtering an `NSArray` according to a custom filter. – Gabriele Petronella Dec 28 '12 at 15:38
  • @Gab - I added the full working code as answer for anyone that maybe interested in future. – Sam B Dec 28 '12 at 15:40
  • Compare is also a string method, so I don't see why this isn't a valid answer. It's not directly and fully showing how to filter the array, but it's showing how to validate each date, just add a loop and you're good to go – Ismael Dec 28 '12 at 16:06
  • 1
    @JayD `compare:` is a perfectly valid method of `NSString` (see [here](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html)), and it performs a lexicographic comparison. Most char sets are constructed so lexicographic ordering works for numbers, too - at least when the number of digits correspond. Further, cocoa even provides an option to interpret numbers of different order of magnitude (`NSNumericSearch`). So while I agree this answer is taking a shortcut, there is nothing factually wrong with it. – Monolo Dec 28 '12 at 16:22
  • @JayD `compare:` is a `NSString` method, too. Ismael's code is fine. – Rob Dec 28 '12 at 16:34
  • Ya you are right, I didn't find campare for NSString in Xcode 4.2 but in xCode 4.5 it was there. – junaidsidhu Dec 29 '12 at 09:56
1

If you can decide the data structures yourself (i.e., they are not an outside requirement), then maybe you would like to consider converting the inner arrays (the records of your data set, so to speak) into dictionaries.

The advantage of using dictionaries for your records is that the Cocoa framework provides all you need to store, filter, and generally hook your data up.

So allow me to propose a set-up as follows:

// For the purposes of this piece of code, a hard coded array of dictionaries: 
NSArray *fullArray = @[
    @{@"desc": @"Toms Bday",    @"bday": [NSDate dateWithString:@"2012-01-10 00:00:00 +00:00"]},
    @{@"desc": @"Jesscia Bday", @"bday": [NSDate dateWithString:@"2012-01-27 00:00:00 +00:00"]}
// etc...
];


// The interval of NSDates that you want to filter
NSDate *intervalBeginning = [NSDate dateWithString:@"2012-01-15 00:00:00 +00:00"];
NSDate *intervalEnd       = [NSDate dateWithString:@"2012-01-31 23:59:59 +00:00"];

// Find the relevant record(s):
NSArray *filteredArray = [fullArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"bday >= %@  and bday <= %@", intervalBeginning, intervalEnd]];

Done!

Monolo
  • 18,205
  • 17
  • 69
  • 103
1

Assuming entries as your NSArray of NSArrays of NSStrings.

NSDateFormatter * formatter = [NSDateFormatter new];
[formatter setDateFormat:@"yyyy-MM-dd"];

NSString * start = @"2012-01-01";
NSDate * startDate = [formatter dateFromString:start];

NSString * end = @"2012-01-31";
NSDate * endDate = [formatter dateFromString:end];

NSIndexSet * indexes = [entries indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    NSString * dateString = obj[1]; // retrieve the date string
    NSDate * date = [formatter dateFromString:dateString];

    return !([date compare:startDate] == NSOrderedAscending ||
             [date compare:endDate] == NSOrderedDescending);
}];

NSArray * results = [entries objectsAtIndexes:indexes];

What I'm doing here is filtering the indices that pass a test (being in between start and end dates) defined by a block and then create an array with just those indices.

Before that, you clearly need to obtain dates from strings using a NSDateFormatter.

I personally like blocks better than predicates, since I consider them easier to read.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
0

Thank you to everyone for helping me out. Here is the code that is working right now in my Kal-Calendar project. I hope this helps anyone that maybe interested in future. Though other people who answered this post had better generic answers

- (void)loadHolidaysFrom:(NSDate *)fromDate to:(NSDate *)toDate delegate:(id<KalDataSourceCallbacks>)delegate
{
    NSLog(@"Fetching B-Days between %@ and %@...", fromDate, toDate);

    NSDateFormatter *fmt = [[[NSDateFormatter alloc] init] autorelease];
    [fmt setDateFormat:@"yyyy-MM-dd"];


    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *stringsPlistPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"birthdays.plist"];

    //READ IN
    NSMutableArray *arryData = [[NSMutableArray alloc] initWithObjects:nil];
    NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:stringsPlistPath];



    for (id key in dictionary) 
    {
        [arryData addObject:[dictionary objectForKey:key]];
    }


    for (int i=0; i<[arryData count]; i++)
    {

        NSMutableArray *bdayArray = [[NSMutableArray alloc] initWithArray:[arryData objectAtIndex:i]];

        NSString *nameStr = [bdayArray objectAtIndex:0];
        NSString *bdayStr = [bdayArray objectAtIndex:1];

        NSLog(@"nameStr:%@ ... bdayStr:%@ ...", nameStr,bdayStr);


        NSDate *BirthdayDate = [fmt dateFromString:bdayStr];

        if ([fromDate compare:BirthdayDate] == NSOrderedAscending &&
            [toDate compare:BirthdayDate] == NSOrderedDescending) 
        {
            // valid date
            //do something interesting here ... and it does!
        }

    }
Sam B
  • 27,273
  • 15
  • 84
  • 121
  • thank you for sharing. I must say that filtering an array with in a for-loop fashion is not really elegant, even if it works. `NSArray` has its specific method - predicate or block based - to perform a filter. I suggest you to check out @Monolo's answer or mine. – Gabriele Petronella Dec 28 '12 at 15:45