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

Ios 从列表中选择最近的3个日期,ios,nsarray,nsdate,nsdictionary,ios5,Ios,Nsarray,Nsdate,Nsdictionary,Ios5,我正在开发一个电视指南应用程序,并试图从NSArray和NSDictionary中获取最近的3个日期。到目前为止还不错,但我一直在努力找出如何以尽可能少的内存和代码(从而降低bug或崩溃的可能性)的最佳方式实现这一点。数组已排序 我有一本字典,里面有一天所有频道的节目。词典保留一个NSDate(称为date)。 假设一个频道有8个节目,现在是11:45。节目3从11:00开始到12:00结束,节目4从12:00开始到13:00结束,节目5从13:00到14:00等。 我怎样才能从我的字典数组中找

我正在开发一个电视指南应用程序,并试图从NSArray和NSDictionary中获取最近的3个日期。到目前为止还不错,但我一直在努力找出如何以尽可能少的内存和代码(从而降低bug或崩溃的可能性)的最佳方式实现这一点。数组已排序

我有一本字典,里面有一天所有频道的节目。词典保留一个NSDate(称为date)。
假设一个频道有8个节目,现在是11:45。节目3从11:00开始到12:00结束,节目4从12:00开始到13:00结束,节目5从13:00到14:00等。
我怎样才能从我的字典数组中找到最快(记忆方面)和最简单的show#3(从过去开始!)、#4和#5

目前,我正在执行for循环,获取每个字典,然后将字典日期与当前日期进行比较。这就是我被困的地方。或者我只是有个脑筋急转弯

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

我是用ARC为iOS 5编写的。

问题是“最快(内存方面)”的问题。您是在寻找最快的还是最有内存/内存占用意识的?对于算法,通常存在空间与时间的折衷,因此当您加快速度时,通常通过添加索引和其他查找数据结构来实现,这会增加内存占用

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

有了额外的存储,您可以有一个额外的数组,它可以索引到时间段(每15分钟一个,粒度是否足够好?),然后菊花链显示这些时间段。给定当前时间,您可以直接索引到当前时间段,然后查找下一组节目。数组将具有指向字典所指向的相同对象的指针。这是一个额外的数据结构,用于优化一种特定的访问模式,但这样做的代价是增加内存

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

最后,您可以将所有节目存储在sqlite数据库或CoreData中,并通过一个查询解决您的问题。让sql引擎来完成这项艰巨的工作。这也会让你的记忆足迹保持合理

希望能激发一些想法

编辑:

这是一个粗略的示例,展示了如何构建一个look表——一个每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;
}

我不确定我是否理解您的模型结构,您有一个
NSArray
节目,每个节目都是一个
NSDictionary
,包含节目日期和其他信息,对吗

然后,一个想法是根据节目开始时间到现在的距离对节目的
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个节目中获得该节目

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
(下一个节目之后的节目)中的对象

显然,在实际操作中,您应该添加一些验证(例如
indexOfNextShow
在尝试访问索引
indexOfNextShow-1
处的对象之前为>0,并且
indexOfNextShow+1
未超过数组中的显示总数)

这样做的好处是,由于您的显示数组已经按startDate排序,
indexOfObjectPassingTest:
返回通过测试的第一个对象,并在找到正确的对象后立即停止迭代。因此,这既简洁易读,又相对高效


.

谢谢您的回复。我认为你写的第二个选择适合我(尽管我还没有完全理解)。SQLLite选项并没有那么好,因为应用程序(目前)确实使用核心数据。它从外部服务获取数据并将其保存在内存中。第一个是我一直在想的,但是没有看到它这么好(因此,慢)。你能给我一个小的代码例子,让我明白你对#2的意思吗?太棒了!现在我明白你的意思了。酷。我要试试看!很好的解决方案;)无论如何,如果您的节目跨越多天运行(插槽循环),或者如果您的阵列包含不同日期的节目列表(插槽过载),请务必小心。要点很好。一个可能的解决方案是在它使用的每个插槽中注册它。另一个优化是,插槽可能只有小时,对象上有偏移量(从小时后5开始)。谢谢!我想你确实误解了我,所以我更新了我的问题。数组已按显示的开始顺序排序。我需要的是目前正在播出的节目+接下来的两个节目。所以,如果现在的时间是11:45,我希望结果是
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);
}];
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
}];
// in practice you should check the range validity before doing this
NSIndexSet* indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(indexOfNextShow-1,3)];
NSArray* onAirShowAnd2Next = [arrayOfShows objectsAtIndexes:indexes];