views:

747

answers:

4

Hello,

I have an NSArray of DiaryEntry objects, where each DiaryEntry has an NSDate date_ field.

I want to display all of the DiaryEntrys in a table view, grouped by the day of the week, where each group is sorted by date in ascending order.

So, I need to take the NSArray, and convert to an NSDictionary of arrays, where the key is the day of the week (NSDate), and the value is an NSArray of DiaryEntrys, ordered by the date_ field in ascending order.

I figure this is a pretty common operation, but can't find any sample code anywhere.

Any help is greatly appreciated.

Thanks!!

A: 

Well, I assume your DiaryEntry has a date property. Here's a quick and dirty version, you could make this a lot nicer.

NSMutableDictionary *map = [[[NSMutableDictionary alloc] init] autorelease];
NSMutableArray *array;
for (DiaryEntry *entry in myArray) {
    array = [map objectForKey:entry.date];
    if (!array) {
        array = [[[NSMutableArray alloc] init] autorelease];
        [map setObject:array forKey:entry.date];
    }
    [array addObject:entry];
}

I'd double check the code for method names/compilation... I'm sort of winging it here however it's basically:

Go through the list. For each entry you find, see if there's an array associated with that date. If not, create it. Add to that array.

Note You may want to consider changing the structure to an array... unless you live in a land with more than 7 days, you can store an array stored in a particular order. I try to avoid map structures as much as possible unless I have a ton of objects and I want the quick lookup.

Malaxeur
Thanks, Malaxeur. One complication; there can be multiple diary entries for each day.
Jacko
Right. So it's an NSDictionary of arrays. Where each entry in the dictionary represents a day, and the array within the dictionary represents each DiaryEntry for that day.
Malaxeur
A: 

Hi Jacko,

I didn't test it, but I could wrote this piece of code that you can improve as you need.


// Ordering the array ascending
            myArray = [myArray sortedArrayUsingDescriptors:
                                [NSArray arrayWithObject: 
                                 [[[NSSortDescriptor alloc] initWithKey:@"date_ field"
                                                              ascending:YES
                                                               selector:@selector(compare:)] autorelease]]];

            NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
            NSMutableArray *arrayForCurrentDate = [[NSMutableArray alloc] init];
            NSString *lastDate = [myArray objectAtIndex:0];
            for (int i=0; i< [myArray count]; i++)
            {
                if (![lastDate isEqualToString:[myArray objectAtIndex:i])
                {
                    if ([arrayForCurrentDate count])
                        [myDictionary setObject:arrayForCurrentDate forKey:lastDate];
                    [arrayForCurrentDate removeAllObjects];
                    lastDate [myArray objectAtIndex:i];
                }
                [arrayForCurrentDate addObject:];
            }
            if ([arrayForCurrentDate count])
                  [myDictionary setObject:arrayForCurrentDate forKey:lastDate];

Cheers,
VFN

vfn
Thanks, VFN. My situation is a little more complicated: there can be multiple Diary entries for a particular day. So, lastDate changes when the day of the week changes.
Jacko
Hey Jacko, this is the idea for "lastDate", while the day is the same, you save everything on the same array, when the day change you save the array on your dictionary, and than clean the array to start with the info for the new day. Where isn't it clear in the code? You can change wherever you want, it is only you adapt the code for your usage.
vfn
A: 

The following should work (did not actually compile and test the code)

            NSEnumerator* enumerator;
            DiaryEntrys* currEntry;
            NSMutableDictionary* result;

            /*
             use sorting code from other answer, if you don't sort, the result will still contain arrays for each day of the week but arrays will not be sorted
             */
            [myArray sortUsingDescriptors:....];
            /*
             result holds the desired dictionary of arrays
             */
            result=[[NSMutableDictionary alloc] init];
            /*
             iterate throught all entries
             */
            enumerator=[myArray objectEnumerator];
            while (currEntry=[enumerator nextObject])
            {
                NSNumber* currDayOfTheWeekKey;
                NSMutableArray* dayOfTheWeekArray;
                /*
                 convert current entry's day of the week into something that can be used as a key in an dictionary
                 I'm converting into an NSNumber, you can choose to convert to a maningfull string (sunday, monday etc.) if you like
                 I'm assuming date_ is an NSCalendarDate, if not, then you need a method to figure out the day of the week for the partictular date class you're using

                 Note that you should not use NSDate as a key because NSDate indicates a particular date (1/26/2010) and not an abstract "monday"
                 */
                currDayOfTheWeekKey=[NSNumber numberWithInt:[[currEntry valueForKey:@"date_"] dayOfWeek]];
                /*
                 grab the array for day of the week using the key, if exists
                 */
                dayOfTheWeekArray=[result objectForKey:currDayOfTheWeekKey];
                /*
                 if we got nil then this is the first time we encounter a date of this day of the week so
                 we create the array now
                 */
                if (dayOfTheWeekArray==nil)
                {
                    dayOfTheWeekArray=[[NSMutableArray alloc] init];
                    [result setObject:dayOfTheWeekArray forKey:currDayOfTheWeekKey];
                    [dayOfTheWeekArray release];    // retained by the dictionary
                }
                /*
                 once we figured out which day the week array to use, add our entry to it.
                 */
                [dayOfTheWeekArray addObject:currEntry];
            }
Eyal Redler
Thanks, Eyal. I hadn't used enumerators before, will use this in my code.
Jacko
A: 

So, here is my final result. I actually had to bucket my objects by day and month, rather than just by the day of week (not clear in original posting):

(entries is the original array)

// reverse sort of entries, and put into daily buckets

-(NSMutableArray*) sortedEntries{

if (sortedEntries == nil) {
    NSInteger currentDay = -1;
    NSCalendar *gregorian = [NSCalendar currentCalendar];
    NSEnumerator* enumerator = [self.entries reverseObjectEnumerator];
    sortedEntries = [NSMutableArray new];
    DiaryEntry* currentEntry;
    while (currentEntry=[enumerator nextObject])
    {
        NSDate* date = [currentEntry valueForKey:@"date"];
        NSDateComponents *weekdayComponents = [gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit) fromDate:date];

        NSInteger newDay = [weekdayComponents day];
        if (currentDay == -1 || newDay != currentDay){
            NSMutableArray* dailyArray = [NSMutableArray new];  
            [sortedEntries addObject:dailyArray];
            [dailyArray addObject:currentEntry];
            [dailyArray release];

        } else {
            [[sortedEntries lastObject] addObject:currentEntry];
        }

        currentDay = newDay;
    }
}

return sortedEntries;

}

Jacko