在字符串数组中查找最近的日期

Posted

技术标签:

【中文标题】在字符串数组中查找最近的日期【英文标题】:Find nearest date in string array 【发布时间】:2017-06-28 18:20:41 【问题描述】:

所以,我有一个已排序的NSArray,其中包含NSString 对象(从服务器下载),格式为:yyyy-MM-dd

差不多是这样的:

NSArray <NSString *> *dates = @[@"2017-06-25",
                                @"2017-06-26",
                                @"2017-06-27",
                                @"2017-06-28",
                                @"2017-06-30",
                                @"2017-07-01",
                                @"2017-07-02",
                                @"2017-07-03"];

所以,今天是 2017-06-29,它不在数组中。如何获得下一个最近的?在这个示例中是 06-30,但如果 06-30 不存在,它可能是 07-01...

更新

所以人们都在问我我试图做什么。所以就是这样(不是很有效,但是工作)

    查找今天是否在数组中(如果是,则返回)

    循环日期:

    2.1 将dateString 转换为date

    2.2 比较date是否大于today =>如果是则返回

    如果在第 2 步中没有找到,则返回日期数组中的最后一个对象。

实际代码:

NSDateFormatter *formatter = [NSDateFormatter new];
formatter.dateFormat = @"yyyy-MM-dd";

NSDate *today = [NSDate date];
NSUInteger index = [dates indexOfObject:[formatter stringFromDate:today]];

// Step 1
if (index == NSNotFound) 

    // Step 2: Loop converted
    NSInteger i = 0;
    for (NSString *date in dates) 

        // Step2.1: find the next nearest date's index
        NSDate *convertedDate = [formmater dateFromString:date];

        // Step2.2: Compare
        if ([convertedDate intervalSinceDate:today] > 0) 
            index = i;
            break;
        

        i++;
    

    // Step 3: Still not found, index = last index
    if (index == NSNotFound) index = i-1;


return dates[index];

这看起来不太好,因为我可能会重新加载日期数组。我能有更好的解决方案吗?

【问题讨论】:

到目前为止你尝试过什么?请添加您的代码。一种简单的方法是遍历数组并计算两个日期之间的时间。存储差异最小的日期。当然,如果您知道列表是预先排序的,那么您可以进行明显的优化(忽略检查日期之前的任何日期,并忽略除检查日期之后差异最小的日期之外的任何日期)。 NSDateFormatter 至少将日期转换为NSDate。那么,您到底在寻找什么?未来最接近?过去最接近?最接近什么?你有什么尝试? @RoboticCat 我不知道如何找到它。在我的代码中,我只是检查是否找到了今天。数组已排序。你认为我必须将整个数组转换为NSDate,今天添加,再次排序然后找到最近的吗? @Larme 要求查找下一个最近的,即未来 @Eddie 定义“更好”的答案。只是因为它循环了两次,所以在你看来它“更慢”。伙计,为您的代码降低一些指标。查看内存和时间使用情况,然后回过头来说明为什么您的解决方案或其他解决方案更糟。下面列出的答案很好,可以完成工作。 【参考方案1】:

您的算法还不错,尽管您的代码似乎没有实现它(没有排序?)。如果您想改进它,请考虑:

首先进行第一次扫描以检查精确匹配可能没有什么意义 - 这可能是通过无序数组进行的线性搜索(由indexOfObject: 实现),如果失败,您必须再次扫描近距离比赛,同时进行。

其次排序没有优势,充其量是O(NlogN),作为线性搜索,O(N)会找到你需要的答案。

这是一个草图:

    将您要搜索的日期从NSString 转换为NSDate,称之为targetbestMatchNSString 设置为nil。将bestDeltaNSTimeInterval 设置为最大可能值DBL_MAX

    遍历您的 dates 数组:

    3.1。将字符串日期转换为NSDate,比如date

    3.2。将delta 设置为datetarget 之间的差异

    3.3。如果delta 为零,则表示完全匹配,返回它

    3.4。如果delta 优于bestDelta,则更新bestDeltabestMatch

    迭代后bestMatch 是最佳匹配,如果没有则nil

这是一个单次迭代,O(N),提前返回完全匹配。

HTH

【讨论】:

检查更新的算法。看来我的和你的一样。我已经更新它以匹配我下面的代码。 您仍在对阵列进行两次传递。您现在已经声明数组已排序,因此可以对上述算法进行简单改进,以便在找到超过目标日期的日期后终止。所以这两种算法并不相同,但在实践中差异可能很小。 所以,你的意思是我应该删除indexOfObject: 并执行循环? 请参阅答案中的步骤 3.3。并且您引入的先决条件是数组已排序,步骤 3.4 不再需要跟踪最佳增量,第一个 +ve 增量是最好的,这也意味着这两个步骤可以简单地组合......此外正在排序的数组允许将线性迭代替换为 二分搜索,从而将复杂性降低到 O(logN) - 但是这样的更改只值得数组很大并且可能在您的情况下过度杀伤,尽管我现在看到您无论如何都选择了更复杂的解决方案,所以可能不会!希望这次探索对您有所帮助。 取决于您对复杂的定义;-) 一个使用谓词(内存分配)、一个块(以及更多)、创建一个辅助数组(甚至更多)等;另外两个是提前退出的简单循环。无论如何,您已经欣赏了许多答案和讨论,这很好!保持好奇的心态,它会为您服务。【参考方案2】:

请为您的问题找到最简单的解决方案。 更新了基于排序顺序的解决方案!

我们可以使用 NSPredicate Block 来解决。

    static NSDateFormatter* formatter = nil;
    static NSDate* today = nil;


    // return an NSDate for a string given in yyyy-MM-dd
    - (NSDate *)dateFromString:(NSString *)string 
        if (formatter == nil) 
            formatter = [NSDateFormatter new];
            formatter.dateFormat = @"yyyy-MM-dd";
        
        return [formatter dateFromString:string];
    

    // Helps to return today date.
    -(NSDate*) getTodayDate 
        if (today == nil) 
            today = [NSDate date];
        
        return today;
    

            // Helps to find nearest date from Array using Predicate
    -(NSString*)findNearestDate:(NSArray*)dateArray 
        today = nil;


        NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *dateString, NSDictionary *bind)
            // this is the important part, lets get things in NSDate form so we can use them.
            NSDate *dob = [self dateFromString:dateString];
            NSComparisonResult result = [[self getTodayDate] compare:dob];
            if (result == NSOrderedSame || result == NSOrderedAscending) 
                return true;
            
            return false;
        ];


        // Apply the predicate block.
        NSArray *futureDates = [dateArray filteredArrayUsingPredicate:predicate];
        if ([futureDates count] > 0) 
            // Sort the Array.
            futureDates = [futureDates sortedArrayUsingSelector: @selector(compare:)];
            return [futureDates objectAtIndex:0];
        
        return nil;
    

    NSArray <NSString *> *dates = @[@"2017-06-25",
                                    @"2017-06-26",
                                    @"2017-06-27",
                                    @"2017-06-28",
                                    @"2017-06-30",
                                    @"2017-07-01",
                                    @"2017-07-02",
                                    @"2017-07-03"];

    NSLog(@"Nearest Date: %@", [self findNearestDate:dates]);

答案:最近日期:2017-06-30

【讨论】:

既然可以得到 dateInterval 并与 0 比较,为什么还要使用 compare? 我在谓词中使用 NSComparisonResult。它将帮助我们找到订单。 我认为我们可以像这样使用日期比较并且工作几乎相同:return [[self getDateToday] intervalSinceDate:dob] &gt;= 0; 另外,如果我们使用 [dateArray indexOfObjectPassingTest:] 块谓词会不会更好? 是的 >= 等于结果 == NSOrderedSame ||结果 == NSOrderedAscending。 请找到更新的答案,您将在排序日期方面为您提供帮助。【参考方案3】:

1。输入

所以你有一个这样的NSString 数组

// input
NSArray<NSString *> * words = @[@"2017-06-25",
                                @"2017-06-26",
                                @"2017-06-27",
                                @"2017-06-28",
                                @"2017-06-30",
                                @"2017-07-01",
                                 @"2017-07-02",
                                 @"2017-07-03"];

2。将NSString的数组转换成NSDate的数组

首先你需要将每个输入字符串转换成一个 NSDate

NSMutableArray<NSDate *> * dates = [NSMutableArray new];
NSDateFormatter * dateFormatter = [NSDateFormatter new];
dateFormatter.dateFormat = @"yyyy-MM-dd";
for (NSString * word in words) 
    [dates addObject:[dateFormatter dateFromString:word]];

3。寻找nearestDate

现在您可以找到最近的日期

NSDate * nearestDate = nil;
NSTimeInterval deltaForNearesttDate = 0;
NSDate * now = [NSDate new];
for (NSDate * date in dates) 
    NSTimeInterval delta = fabs([date timeIntervalSinceDate:now]);
    if (nearestDate == nil || (delta < deltaForNearesttDate)) 
        deltaForNearesttDate = delta;
        nearestDate = date;
    

4。结论

结果进入nearestDate变量so

NSLog(@"%@", nearestDate);

2017 年 6 月 28 日星期三 00:00:00

【讨论】:

这看起来比我的解决方案差得多:|你必须循环两次才能找到,而我的只有一次(甚至可能没有完全循环) @Eddie 你问过How do I get the next nearest one?。然后你用你自己的代码更新了你的问题。那你到底在问什么?比您的循环更少的解决方案? 对不起,有人要我的代码,所以我添加了它们。是的,你的回答完成了工作。但是你能想到另一个比我更好的吗?我在为这个问题寻求一个好的解决方案 @Eddie:我认为indexOfObject: 也有一个循环(它会迭代直到找到对象)。所以你实际上也做了 2 个循环。

以上是关于在字符串数组中查找最近的日期的主要内容,如果未能解决你的问题,请参考以下文章

按即将到来的日期对日期为字符串的数组进行排序

在 Postgres 9.4 中查找 JSON 数组中的最后一项

在给定的字符串列表中查找字符串的所有字谜

MATLAB查找字符数组中是不是存在特定的字符串

在字符串和结构数组中查找动态内存分配错误

在数组中查找字符串