从列表中挑选最近的3个日期

时间:2011-10-01 20:42:33

标签: ios nsarray nsdate nsdictionary ios5

我正在制作一个电视指南应用程序,并且我正在尝试使用NSDictionary来获取NSArray中最接近的3个日期。到目前为止一切都那么好,但我一直试图弄清楚如何使用尽可能少的内存和尽可能少的代码(因此减少错误或崩溃的可能性)以最佳方式执行此操作。该数组已经排序。

我有一本包含所有频道节目的词典,为期一天。字典包含一个NSDate(称为日期) 让我们说一个频道有8个节目,时间现在是11:45。 show#3从11:00开始,到12:00结束,show#4从12:00开始,到13:00结束,#13出现在13:00到14:00等。 我怎么能从我的字典数组中获取show#3 (以前开始的!),#4和#5最快(内存方面)最简单?

目前我正在进行for循环获取每个字典,然后将字典日期与当前日期进行比较。那就是我被困住的地方。或者也许我只是有一个大脑fag。

我目前的代码(经过一段时间测试不同的东西):

- (NSArray*)getCommingProgramsFromDict:(NSArray*)programs amountOfShows:(int)shows
{
    int fetched = 0;
    NSMutableArray *resultArray = [[NSMutableArray alloc] init];
    NSDate *latestDate = [NSDate date];

    for (NSDictionary *program in programs)
    {
        NSDate *startDate = [program objectForKey:@"date"];

        NSLog(@"Program: %@", program);
        switch ([latestDate compare:startDate]) {
            case NSOrderedAscending:
                NSLog(@"latestDate is older, meaning the show starts in the future from latestDate");
                // do something
                break;
            case NSOrderedSame:
                NSLog(@"latestDate is the same as startDate");
                // do something
                break;
            case NSOrderedDescending:
                NSLog(@"latestDate is more recent, meaning show starts in the past");
                // do something
                break;
        }

        // Now what?
    }

    return resultArray;
}

我正在使用ARC为iOS 5编写它。

3 个答案:

答案 0 :(得分:1)

这个问题要求“最快(记忆明智)”。您是否正在寻找最快或最具记忆力/足迹的人?对于算法,通常会有空格与时间的权衡,因此当您加快速度时,通常会通过添加索引和其他查找数据结构来增加内存占用量。

对于这个问题,直接实现将迭代通过每个通道,每个项目将每个通道与内存中保存的前3个进行比较。但那可能很慢。

使用额外的存储空间,你可以有一个额外的阵列,它可以编入时间段(每15分钟一个粒度就足够了吗?),然后菊花链显示这些时隙。根据当前时间,您可以直接索引到当前时段,然后查找下一组节目。该数组将具有指向字典指向的相同对象的指针。这是一种额外的数据结构,可以优化一种特定的访问模式,但它可以节省成本 - 更多内存。

这会增加你的足迹,但会非常快,因为它只是一个数组索引偏移量。

最后,您可以将所有节目存储在sqlite数据库或CoreData中,并使用一个查询解决您的问题。让sql引擎做的努力。这也将使你的记忆足迹合理。

希望能引发一些想法。

编辑:

一个粗略的例子,展示了如何构建一个外观表 - 一个每15分钟有一个插槽的数组。它是瞬间跳转到当前时隙,因为它只是一个数组偏移。然后你走了绝对的步行数 - 接下来的三个你就走了。所以,它是一个3次迭代的数组偏移量。

大多数代码都是建立日期 - 查找表,查找时间段和循环是微不足道的。

NSInteger slotFromTime(NSDate *date)
{
    NSLog(@"date: %@", date);

    NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:(NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:date];
    NSInteger hour = [dateComponents hour];
    NSInteger minute = [dateComponents minute];
    NSInteger slot = (hour * 60 + minute)/15;
    NSLog(@"slot: %d", (int)slot);

    return slot;
}

int main (int argc, const char * argv[])
{
    // An array of arrays - the outer array is an index of 15 min time slots.
    NSArray *slots[96];
    NSDate *currentTime = [NSDate date];
    NSInteger currentSlot = slotFromTime(currentTime);

    // populate with shows into the next few slots for demo purpose
    NSInteger index = currentSlot;
    NSArray *shows1 = [NSArray arrayWithObjects:@"Seinfeld", @"Tonight Show", nil];
    slots[++index] = shows1;
    NSArray *shows2 = [NSArray arrayWithObjects:@"Friends", @"Jurassic Park", nil];
    slots[++index] = shows2; 

    // find next three -jump directly to the current slot and only iterate till we find three.
    // we don't have to iterate over the full data set of shows
    NSMutableArray *nextShow = [[NSMutableArray alloc] init];
    for (NSInteger currIndex = currentSlot; currIndex < 96; currIndex++)
    {
        NSArray *shows = slots[currIndex];
        if (shows)
        {
            for (NSString *show in shows)
            {
                NSLog(@"found show: %@", show);
                [nextShow addObject:show];
                if ([nextShow count] == 3) 
                    break;
            }
        }

        if ([nextShow count] == 3) 
            break;        
    }

    return 0;
}

输出:

2011-10-01 17:48:10.526 Craplet[946:707] date: 2011-10-01 21:48:10 +0000
2011-10-01 17:48:10.527 Craplet[946:707] slot: 71
2011-10-01 17:48:14.335 Craplet[946:707] found show: Seinfeld
2011-10-01 17:48:14.336 Craplet[946:707] found show: Tonight Show
2011-10-01 17:48:21.335 Craplet[946:707] found show: Friends

答案 1 :(得分:1)

我不确定我是否理解你的模型结构,你有一个NSArray个节目,每个节目都是NSDictionary持有节目的NSDate以及其他信息,对吗?

然后,一个想法是根据节目开始时间与现在之间的距离对节目NSArray进行排序。

NSArray* shows = ... // your arraw of NSDictionaries representing each show
NSArray* sortedShows = [shows sortedArrayUsingComparator:^(id show1, id show2) {
    NSTimeInterval ti1 = fabs([[show1 objectForKey:@"startDate"] timeIntervalSinceNow]);
    NSTimeInterval ti2 = fabs([[show2 objectForKey:@"startDate"] timeIntervalSinceNow]);
    return (NSComparisonResult)(ti1-ti2);
}];

当然,在这一点上很容易只采用sortedShows数组的3个第一个节目。

如果我误解了您的模型结构,请编辑您的问题以指定它,但我确信您可以调整我的代码以适应您的模型

答案 2 :(得分:1)

在您的编辑和解释之后,这是另一个答案,希望更好地适合您的问题。

想法是找到下一个节目的索引(现在是startDate)。一旦你拥有它,很容易在上一个索引(播出时)和后面的2个节目上获得节目。

NSUInteger indexOfNextShow = [arrayOfShows indexOfObjectPassingTest:^BOOL(id program, NSUInteger idx, BOOL *stop) {
    NSDate* startDate = [program objectForKey:@"date"];
    return ([startDate timeIntervalSinceNow] > 0); // startDate after now, so we are after the on-air show
}];

在该阶段,indexOfNextShow包含NSArray中将在当前节目播出后播放的节目索引。因此,根据您的问题,您想要的是索引为indexOfNextShow-1(在空中展示),indexOfNextShow(下一个节目)和indexOfNextShow+1(在下一个节目后显示)的对象。

// in practice you should check the range validity before doing this
NSIndexSet* indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(indexOfNextShow-1,3)];
NSArray* onAirShowAnd2Next = [arrayOfShows objectsAtIndexes:indexes];

显然,在实际操作中,您应该尝试添加一些验证(例如indexOfNextShow为&gt; 0,然后尝试访问索引为indexOfNextShow-1的对象,而indexOfNextShow+1未超过您的展示总数数组)。

这样做的好处是,由于你的节目数组已经由startDate排序,indexOfObjectPassingTest:返回通过测试的第一个对象,并在找到正确的对象后立即停止迭代。所以这是简洁,易于阅读的代码和相对有效的。