返回日期区间的ArrayList中重叠的数量

3
我有一个名为$all_data的数组。
Array
(
[Chevenez] => Array
    (
        [41.NEwan0] => Array
            (
                [0] => Array
                    (
                        [0] => 2022-01-19 03:53:37.49459
                        [1] => 2022-01-19 04:53:37.49459
                    )

                [1] => Array
                    (
                        [0] => 2022-01-09 03:53:37.49459
                        [1] => 2022-01-09 04:53:37.49459
                    )

            )

        [41.NEwan1] => Array
            (
                [0] => Array
                    (
                        [0] => 2022-01-19 04:23:37.49459
                        [1] => 2022-01-19 05:23:37.49459
                    )

                [1] => Array
                    (
                        [0] => 2022-01-09 04:23:37.49459
                        [1] => 2022-01-09 05:23:37.49459
                    )

            )

        [42.NEwan0] => Array
            (
                [0] => Array
                    (
                        [0] => 2022-01-19 04:03:37.49459
                        [1] => 2022-01-19 04:33:37.49459
                    )

                [1] => Array
                    (
                        [0] => 2022-01-09 04:03:37.49459
                        [1] => 2022-01-09 04:33:37.49459
                    )

            )

        [42.NEwan1] => Array
            (
                [0] => Array
                    (
                        [0] => 2022-01-19 04:13:37.49459
                        [1] => 2022-01-19 05:13:37.49459
                    )

                [1] => Array
                    (
                        [0] => 2022-01-09 04:13:37.49459
                        [1] => 2022-01-09 05:13:37.49459
                    )

            )

    )

[Barcelona] => Array
    (
        [5.NEwan0] => Array
            (
                [0] => Array
                    (
                        [0] => 2022-01-19 03:53:37.49459
                        [1] => 2022-01-19 04:53:37.49459
                    )

            )

        [5.NEwan1] => Array
            (
                [0] => Array
                    (
                        [0] => 2022-01-19 04:23:37.49459
                        [1] => 2022-01-19 05:23:37.49459
                    )

            )

        [16.NEwan0] => Array
            (
                [0] => Array
                    (
                        [0] => 2022-01-19 04:03:37.49459
                        [1] => 2022-01-19 04:33:37.49459
                    )

            )

        [16.NEwan1] => Array
            (
                [0] => Array
                    (
                        [0] => 2022-01-19 04:13:37.49459
                        [1] => 2022-01-19 05:13:37.49459
                    )

            )

    )

)

从上述数组中,我正在尝试获取 ChevenezBarcelona 的重叠时间间隔。例如,对于 Chevenez ,如果所有键41.NEwan0,41.NEwan1,42.NEwan0和42.NEwan1都有重叠,则才予以考虑,否则不考虑。
我尝试了此链接中提出的想法。
// Placeholder array to contain the periods when everyone is available.
$periods = [];

foreach($all_data as $key => $data){
    // Loop until one of the people has no periods left.
    while (count($data) && count(array_filter($data)) == count($data)) {
        // Select every person's earliest date, then choose the latest of these
        // dates.
        $start = array_reduce($data, function($carry, $ranges) {
            $start = array_reduce($ranges, function($carry, $range) {
                // This person's earliest start date.
                return !$carry ? $range[0] : min($range[0], $carry);
            });
            // The latest of all the start dates.
            return !$carry ? $start : max($start, $carry);
        });

        // Select each person's range which contains this date.
        $matching_ranges = array_filter(array_map(function($ranges) use($start) {
            return current(array_filter($ranges, function($range) use($start) {
                // The range starts before and ends after the start date.
                return $range[0] <= $start && $range[1] >= $start;
            }));
        }, $data));

        // Find the earliest of the ranges' end dates, and this completes our
        // first period that everyone can attend.
        $end = array_reduce($matching_ranges, function($carry, $range) {
            return !$carry ? $range[1] : min($range[1], $carry);
        });

        // Add it to our list of periods.
        $periods[$key][] = [$start, $end];

        // Remove any availability periods which finish before the end of this
        // new period.
        array_walk($data, function(&$ranges) use ($end) {
            $ranges = array_filter($ranges, function($range) use($end) {
                return $range[1] > $end;
            });
        });
    }
}

// Output the answer in the specified format.
foreach ($periods as $key => $period) {
    foreach ($period as $period1) {
        echo "$key : $period1[0] -> $period1[1]\n";
    }
}

但它给了我以下输出结果。
Chevenez : 2022-01-09 04:23:37.49459 -> 2022-01-09 04:33:37.49459
Chevenez : 2022-01-19 04:03:37.49459 -> 2022-01-19 04:33:37.49459
Barcelona : 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459

但我希望得到的输出结果是什么。

Chevenez : 2022-01-09 04:23:37.49459 -> 2022-01-09 04:33:37.49459
Chevenez : 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459
Barcelona : 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459

这段内容涉及到IT技术,其中提到了时间的错误,应该将第二行中的时间改为 Chevenez: 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459。此外,用户 @amirreza-noori 报告说他在以下数组中遇到了错误。

$all_data = [ 
'Chevenez' => [
    '41.NEwan0' => [['2022-01-19 03:53:37.49459', '2022-01-19 04:53:37.49459'],
                            ['2022-01-09 03:53:37.49459', '2022-01-09 04:53:37.49459']],
    '41.NEwan1' => [['2022-01-19 04:23:37.49459', '2022-01-19 05:23:37.49459'], 
                            ['2022-01-09 04:23:37.49459', '2022-01-09 05:23:37.49459']],
    '42.NEwan0' => [['2022-01-19 04:03:37.49459', '2022-01-19 04:33:37.49459'], 
                            ['2022-01-09 04:03:37.49459', '2022-01-09 04:33:37.49459']],
    '42.NEwan1' => [['2022-01-19 04:13:37.49459', '2022-01-19 05:13:37.49459'], 
                            ['2022-01-09 04:13:37.49459', '2022-01-09 05:13:37.49459']],
    '43.NEwan0' => [['2022-01-09 03:53:37.49459', '2022-01-09 04:53:37.49459']],
    '43.NEwan1' => [['2022-01-09 04:23:37.49459', '2022-01-09 05:23:37.49459']],
    '44.NEwan0' => [['2022-01-09 04:03:37.49459', '2022-01-09 04:33:37.49459']],
    '44.NEwan1' => [['2022-01-09 04:28:37.49459', '2022-01-09 05:13:37.49459']],
],

'Barcelona' => [
    '5.NEwan0' => [['2022-01-19 03:53:37.49459', '2022-01-19 04:53:37.49459']],
    '5.NEwan1' => [['2022-01-19 04:23:37.49459', '2022-01-19 05:23:37.49459']],
    '16.NEwan0' => [['2022-01-19 04:03:37.49459', '2022-01-19 04:33:37.49459']],
    '16.NEwan1' => [['2022-01-19 04:13:37.49459', '2022-01-19 05:13:37.49459']]
]
];

对此的期望输出是什么。

Chevenez : 2022-01-09 04:23:37.49459 -> 2022-01-09 04:33:37.49459
Barcelona : 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459

我已经修复并编辑了原始帖子中的答案。 - Matt Raines
这个回答解决了你的问题吗?PHP 判断多个日期时间范围是否重叠 - Matt Raines
@matt-raines 我已经更新了问题并提供了新的代码,但仍存在一些问题。 - Divyesh Jesadiya
是的,恐怕我的另一个问题答案中的代码并不适用于所有情况。感谢您的测试:D但我恐怕没有能力在不使它变得更加复杂的情况下修复它。我会尝试其他答案,如果我有机会再次查看这个问题,我会的。 - Matt Raines
抱歉,我不明白如果所有的键都有重叠怎么办?(在你的问题中)假设对于Chevenez,如果41.NEwan0、41.NEwan1、42.NEwan0和42.NEwan1这些键都有重叠,则只考虑它们,否则就不考虑。 - Ahmed Jaouadi
3个回答

1
我了解到你对待日期的方式。因此没有必要将它们转换成浮点数。所以问题在于算法。以下代码可能是一个解决方案。
$all_data = [ 
    'Chevenez' => [
        '41.NEwan0' => [['2022-01-19 03:53:37.49459', '2022-01-19 04:53:37.49459'],
                                ['2022-01-09 03:53:37.49459', '2022-01-09 04:53:37.49459']],
        '41.NEwan1' => [['2022-01-19 04:23:37.49459', '2022-01-19 05:23:37.49459'], 
                                ['2022-01-09 04:23:37.49459', '2022-01-09 05:23:37.49459']],
        '42.NEwan0' => [['2022-01-19 04:03:37.49459', '2022-01-19 04:33:37.49459'], 
                                ['2022-01-09 04:03:37.49459', '2022-01-09 04:33:37.49459']],
        '42.NEwan1' => [['2022-01-19 04:13:37.49459', '2022-01-19 05:13:37.49459'], 
                                ['2022-01-09 04:13:37.49459', '2022-01-09 05:13:37.49459']],
        '43.NEwan0' => [['2022-01-09 03:53:37.49459', '2022-01-09 04:53:37.49459']],
        '43.NEwan1' => [['2022-01-09 04:23:37.49459', '2022-01-09 05:23:37.49459']],
        '44.NEwan0' => [['2022-01-09 04:03:37.49459', '2022-01-09 04:33:37.49459']],
        '44.NEwan1' => [['2022-01-09 04:28:37.49459', '2022-01-09 05:13:37.49459']],
    ],

    'Barcelona' => [
        '5.NEwan0' => [['2022-01-19 03:53:37.49459', '2022-01-19 04:53:37.49459']],
        '5.NEwan1' => [['2022-01-19 04:23:37.49459', '2022-01-19 05:23:37.49459']],
        '16.NEwan0' => [['2022-01-19 04:03:37.49459', '2022-01-19 04:33:37.49459']],
        '16.NEwan1' => [['2022-01-19 04:13:37.49459', '2022-01-19 05:13:37.49459']]
    ]
];

$periods = [];

foreach($all_data as $place => $parts) {
    // select first times of each place
    $part1_times = $parts[array_key_first($parts)];
    $periods[$place] = [];
    
    // walk on times parts
    foreach($part1_times as $t => $part1_time_range) {
        $range_low = $part1_time_range[0];        
        $range_up = $part1_time_range[1];
        
        // detect common range
        foreach($parts as $part_times) {
            if(!isset($part_times[$t])) break;
            $rlow = $part_times[$t][0];
            $rup = $part_times[$t][1];
            
            if($rlow > $range_low) $range_low = $rlow;
            if($rup < $range_up) $range_up = $rup;          
        }
        
        if($range_low < $range_up) $periods[$place][] = [$range_low, $range_up];
    }
    
    // sort times in each place from small to big
    usort($periods[$place], function($a, $b) {
        return $a[0] > $b[0] ? 1 : -1;
    });
}


// Output the answer in the specified format.
foreach ($periods as $key => $periods) {
    foreach ($periods as $period) {
        echo "$key : $period[0] -> $period[1]\n";
    }
}

输出:

Chevenez : 2022-01-09 04:23:37.49459 -> 2022-01-09 04:33:37.49459
Barcelona : 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459

我在其他情况下遇到了错误,并已更新有关该问题的描述。 - Divyesh Jesadiya
抱歉,Chevenez的一半信息:2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459不应该出现在更新后的数组结果中。 - Divyesh Jesadiya
@DivyeshJesadiya 根据您的评论,答案已经进行了编辑。 - Amirreza Noori

1
我会使用一系列嵌套的循环来完成它。
$output = [];
foreach ($all_data as $place => $place_data) {
    foreach ($place_data as $innerplace_data) {
        $canBeAdded = true;
        foreach ($all_data as $otherplace => $otherplace_data) {
            if ($place !== $otherplace) {
                foreach($otherplace as $innerotherplace_data) {
                    $canBeAdded = $canBeAdded && (!(($innerotherplace_data[0] > $otherplace_data[1]) || ($innerotherplace_data[1] < $otherplace_data[0])));
                }
            }
        }
        if ($canBeAdded) $output[]=$place . " " . $innerplace_data[0] . "->" . $innerplace_data[1];
    }
}

未经测试。


我遇到了“解析错误:语法错误,意外的'$innerplace_data'”错误。 - Divyesh Jesadiya
@DivyeshJesadiya 谢谢您指出了这一点!我的代码中有个打字错误,自那以后已经修复了。现在它工作得更好了吗? - Lajos Arpad
我在“foreach($otherplace as $innerotherplace_data) {”这行仍然遇到错误。 - Divyesh Jesadiya
@DivyeshJesadiya会查看此事并稍后回复(今天晚些时候)。谢谢! - Lajos Arpad
@DivyeshJesadiya 我在编写适当的测试用例时遇到了一些困难。不幸的是,我不能花太多时间在这上面。你能提供一个语法合法的输入数组吗?你在问题中提供的数组可以理解,但是它的语法是不合法的。如果你能构建一个PHP数组作为输入,那就太好了。如果你使用json_encode将你的数组转换成JSON并向我展示结果,那也可以,因为然后我会使用json_decode来获取适当的PHP输入进行测试。 - Lajos Arpad

0
以下是我为您提供的解决方案,经过充分测试:
class TimeSlots {


    private array $cities;

    private array $values;

    private array $overlaps;

    public function __construct(array $data) {
        $this->setData($data);
    }

    /**
     * @param $data
     */
    public function setData($data): void {
        $this->cities = array_keys($data);
        $this->overlaps = [];
        $this->values = array_map("array_values", array_values($data));
    }

    /**
     * @return void
     */
    public function clear(): void {
        $this->values = [];
        $this->cities = [];
    }

    public function getOverlaps(): array {

        if (!empty($this->overlaps)) {
            return $this->overlaps;
        }

        $result = [];
        if (empty($this->cities)) {
            return $result;
        }

        foreach ($this->cities as $position => $city) {
            $computed = $this->computeCityOverlap($position);
            $result[$city] = $computed;
        }

        $this->overlaps = $result;
        return $this->overlaps;
    }

    private function computeCityOverlap(int $pos): array {

        $current = 0;    

        return array_reduce($this->values[$pos],function ($carry, $ranges) use ($pos,&$current) {

            foreach ($ranges as $index => $range) {
                $allRangesFlat = array_filter(array_map(function ($value) use ($index) {
                    return $value[$index] ?? null;
                }, $this->values[$pos]));
            //Don't include current range
                array_splice($allRangesFlat, $current, 1);

                $overlap = $this->inspectOverlap($allRangesFlat, $range);
                if (empty($overlap)) {
                    continue;
                }
                if(empty($carry[$index])){
                    $carry[$index] = [];
                }

                if(empty($start = $carry[$index][0]) || ( $overlap[0] >= max($start, $range[0]))){
                    $carry[$index][0] = $overlap[0];
                }

                if(empty($end = $carry[$index][1]) || ($overlap[1] >= max($end,  $range[1]) )){
                    $carry[$index][1] = $overlap[1];
                }

            }

            $current += 1;
            return $carry;
        }, []);
    }

    private function inspectOverlap(array $haystack, array $range): array{

        if (empty($range[0]) || empty($range[1])) {
            return [];
        }

        $result = array_reduce($haystack, function ($carry, $val) use ($range) {
            if (empty($val[0]) || empty($val[1])) {
                return $carry;
            }

            if ($val[0] >= $range[0] && $val[0] <= $range[1]){

                $end =  min($range[1] , $val[1]);
                if (empty($carry[0]) || ($carry[0] >= $val[0] && $carry[0] <= $val[1])) {
                    $carry[0] = $val[0];
                    $carry[1] =  $carry[1] ? min($end,  $carry[1]) : $end;
                }
            }elseif (($val[1] >= $range[0] && $val[1] <= $range[1])){

                $start = max($val[0], $range[0]);
                if (empty($carry[1]) || ($carry[1] <= $val[1] && $carry[1] >= $val[0])) {
                    $carry[1] = $val[1];
                    $carry[0] =  $carry[0] ? min($start,  $carry[0]) : $start;
                }
            }

            return $carry;
        }, [null, null]);

        return array_filter($result);
    }
}

$all_data = [
    'Chevenez' => [
        '41.NEwan0' => [['2022-01-19 03:53:37.49459', '2022-01-19 04:53:37.49459'],
            ['2022-01-09 03:53:37.49459', '2022-01-09 04:53:37.49459']],
        '41.NEwan1' => [['2022-01-19 04:23:37.49459', '2022-01-19 05:23:37.49459'],
            ['2022-01-09 04:23:37.49459', '2022-01-09 05:23:37.49459']],
        '42.NEwan0' => [['2022-01-19 04:03:37.49459', '2022-01-19 04:33:37.49459'],
            ['2022-01-09 04:03:37.49459', '2022-01-09 04:33:37.49459']],
        '42.NEwan1' => [['2022-01-19 04:13:37.49459', '2022-01-19 05:13:37.49459'],
            ['2022-01-09 04:13:37.49459', '2022-01-09 05:13:37.49459']],
        '43.NEwan0' => [['2022-01-09 03:53:37.49459', '2022-01-09 04:53:37.49459']],
        '43.NEwan1' => [['2022-01-09 04:23:37.49459', '2022-01-09 05:23:37.49459']],
        '44.NEwan0' => [['2022-01-09 04:03:37.49459', '2022-01-09 04:33:37.49459']],
        '44.NEwan1' => [['2022-01-09 04:28:37.49459', '2022-01-09 05:13:37.49459']],
    ],

    'Barcelona' => [
        '5.NEwan0' => [['2022-01-19 03:53:37.49459', '2022-01-19 04:53:37.49459']],
    '    5.NEwan1' => [['2022-01-19 04:23:37.49459', '2022-01-19 05:23:37.49459']],
        '16.NEwan0' => [['2022-01-19 04:03:37.49459', '2022-01-19 04:33:37.49459']],
        '16.NEwan1' => [['2022-01-19 04:13:37.49459', '2022-01-19 05:13:37.49459']]
    ]
];

$slots = new TimeSlots($all_data);
$o = $slots->getOverlaps();
foreach ($o as $city => $periods) {
    foreach ($periods as $period1) {
        echo "$city : $period1[0] -> $period1[1]" . PHP_EOL;
    }
}

输出为:

Chevenez : 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459
Chevenez : 2022-01-09 04:23:37.49459 -> 2022-01-09 04:33:37.49459
Barcelona : 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接