3

I have the following array named $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
                    )

            )

    )

)

From the above array, I am trying to get overlapping time intervals for Chevenez and Barcelona. Let's say for Chevenez if all keys 41.NEwan0, 41.NEwan1, 42.NEwan0 and 42.NEwan1 has overlap then only consider otherwise not.

I have tried the idea presented at this link.

// 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";
    }
}

But it gives me the below output.

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

but my desired output is.

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 gives me Chevenez : 2022-01-19 04:03:37.49459 -> 2022-01-19 04:33:37.49459 in second line but it should be Chevenez : 2022-01-19 04:23:37.49459 -> 2022-01-19 04:33:37.49459

@amirreza-noori I am getting errors for following arrry.

$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']]
]
];

and desired output for it is.

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
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
Divyesh Jesadiya
  • 1,105
  • 4
  • 30
  • 68
  • I've fixed and edited the answer in the original post. – Matt Raines Jan 21 '22 at 14:04
  • Does this answer your question? [PHP Determine when multiple(n) datetime ranges overlap each other](https://stackoverflow.com/questions/36868675/php-determine-when-multiplen-datetime-ranges-overlap-each-other) – Matt Raines Jan 21 '22 at 14:05
  • @matt-raines I've updated the question with a new code but still has some issues. – Divyesh Jesadiya Jan 21 '22 at 15:07
  • Yeah, I'm afraid it doesn't look like the code in my answer to the other question works in all cases. Thanks for testing it :D but I'm afraid I haven't been able to fix it without making it much more complicated. I would try one of the other answers and if I get a chance to look at this question again some time, I will. – Matt Raines Jan 21 '22 at 17:05
  • Sorry I didn't understand something if all keys has overlap Then consider what ? (In your question) : Let's say for Chevenez if all keys 41.NEwan0, 41.NEwan1, 42.NEwan0 and 42.NEwan1 has overlap then only consider otherwise not. – Ahmed Jaouadi Jan 28 '22 at 16:48

3 Answers3

1

I found out the way you treat the dates. So there's no need to convert them to the float number. So the question is about algorithms. The following code could be a solution.

$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";
    }
}

Output:

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
Amirreza Noori
  • 1,456
  • 1
  • 6
  • 20
1

I would do it with a bunch of nested loops

$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];
    }
}

Untested.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • I am getting Parse error: syntax error, unexpected '$innerplace_data' error – Divyesh Jesadiya Jan 31 '22 at 13:33
  • @DivyeshJesadiya thanks for pointing that out! There was a typo in my code, which was fixed since then. Is it working better now? – Lajos Arpad Jan 31 '22 at 13:39
  • I am still getting error on "foreach($otherplace as $innerotherplace_data) {" this line – Divyesh Jesadiya Jan 31 '22 at 13:53
  • @DivyeshJesadiya will look into this and will respond afterwards (later today). Thanks! – Lajos Arpad Jan 31 '22 at 14:12
  • @DivyeshJesadiya I had some difficulty in writing a proper test case. Unfortunately I cannot afford too much time into this. Can you provide an input array that is syntactically legal? Your array in the question can be understood, but it's syntactically illegal. If you could build a PHP array that can be used as an input, that would be great. If you do a `json_encode`, converting your array into a JSON and show me the result, that could also work, because then I would do a `json_decode` to get the proper PHP input to test with. – Lajos Arpad Jan 31 '22 at 15:22
0

You Find down below my solution for you. well tested:

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;
    }
}

Output is:

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
Ahmed Jaouadi
  • 213
  • 3
  • 10