【问题标题】:PHP Find Gaps over the course of N years across multiple date rangesPHP在多个日期范围内查找N年的差距
【发布时间】:2016-12-14 23:03:44
【问题描述】:

取多维数组如

array(
    array('begin' => '2006-01-01', 'finish' => '2006-02-28'),
    array('begin' => '2006-03-01', 'finish' => '2006-06-30'),
    array('begin' => '2006-08-01', 'finish' => '2007-12-30'),
    array('begin' => '2007-01-01', 'finish' => '2016-12-30'),
);

我正在尝试找出处理具有不同程度范围和重叠的第 N 个数组的最佳方法,以查看在 N 年的过程中是否存在差距。我目前的要求是一个月。但我目前根本无法解决这个问题。无需经历一系列嵌套的 foreaches,这些嵌套的 foreaches 最终将我画在一个角落里,并且在更大的数据集上处理成本很高。

【问题讨论】:

  • N 个元素将按照您的示例中的顺序排列?
  • 订单号不能 100% 保证。如果需要,我可能需要采取额外的步骤以某种方式对其进行排序
  • 您希望如何在示例中定义/返回“间隙”?只是“有一个差距是/否”或“有 X 个差距”或“2006-06-30 和 2006-08-01 之间有一个差距”?
  • 理想情况下知道差距是什么,这样我就可以根据用户体验需求展示它们,但在这一点上说是/否是我可以处理的事情。我一直在努力思考这个问题,但我要么想多了,要么它会比它应该的要贵得多,要么它不会给出结果我想要整体

标签: php datetime range


【解决方案1】:

此代码希望在 O(n) 中,并假设它是一个有序数组,如示例中所示。

我使用这种日期格式可以作为字符串进行比较并给出相同的结果,就像您使用复杂的日期对象一样。

// $a is your multidimensional array as above

$gap = array();
for($i=1; $i<sizeof($a); $i++){
   $gap[$i] = $a[$i-1]['finish'] < $a[$i]['begin'];
}

$gap 包含一个数组或布尔值,它们指示在哪些索引处存在间隙。

【讨论】:

  • 是的,这些将作为字符串进行比较,但我对总体上的任何想法持开放态度。
  • 连续天数算作间隔吗? (例如在 30 日结束,从 31 日开始)如果没有,我在代码中遗漏了一些内容。
  • 我希望他们这样做,但目前可以接受到月份。
【解决方案2】:

我不会建议实际的实现,而是更通用的方法。

你通常可以通过两种方式看待这类问题:

  1. 制作超级智能代码。设计一种算法,递归合并重叠的范围,为您留下一系列离散范围。如果数组中有不止一行,则存在间隙,并且间隙定义在范围的结尾和另一范围的开头之间。这里的关键字是recursively
  2. 制作超级愚蠢的代码。构建一个 assoc 数组,以间隔的所有月份(不可能很多,即使 10 年也只有 120 个月)作为键,并将“真”作为值。迭代您的数组,并将出现在某个范围内的月份设置为“false”。使用 array_filter 和 ta-dah!你剩下的几个月有差距。这里的关键是不要使用date 相关的函数(它们很慢),而只是算术。

希望这有助于您走上正轨。

【讨论】:

    【解决方案3】:

    没有经过广泛测试,需要一些重构,但这是我能想到的最好的算法(实际上我已经对数据库做了类似的事情):

    define('DAY_SEC', 86400);
    $result = [];
    foreach ($dates as $date) {
        $begin  = strtotime($date['begin']);
        $finish = strtotime($date['finish']);
    
        $merged = null;
        foreach ($result as $idx => $span) {
            if ($span['begin'] <= $finish + DAY_SEC && $span['finish'] >= $begin - DAY_SEC) {
                if (isset($merged)) {
                    $min = min($span['begin'], $begin, $result[$merged]['begin']);
                    $max = max($span['finish'], $finish, $result[$merged]['finish']);
                    unset($result[$idx]);
                } else {
                    $min = min($span['begin'], $begin);
                    $max = max($span['finish'], $finish);
                    $merged = $idx;
                }
    
                $result[$merged] = ['begin' => $min, 'finish' => $max];
            }
        }
    
        if (!isset($merged)) {
            $result[] = ['begin' => $begin, 'finish' => $finish];
        }
    }
    
    foreach ($result as &$span) {
        $span['begin'] = date('Y-m-d', $span['begin']);
        $span['finish'] = date('Y-m-d', $span['finish']);
    }
    

    这将导致一系列连续的时间跨度。它一一添加时间跨度,并在与所有先前添加的重叠时合并它们。如果它重叠多个时段,则它将连续匹配合并到第一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-17
      • 1970-01-01
      • 2016-09-22
      • 2016-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多