phpif-statementmultidimensional-arraytimeoverlapping-matches

How can I check for time span conflicts on each day?


Class A Time:

Day 1 => 08:00-09:00 | 11:00-12:00 | 13:00-15:00 | 20:00-21:00
Day 2 => 12:00-13:00 | 14:00-15:00 
Day 3 => 08:00-09:00 | 16:00-17:00 | 18:00-19:00
Day 4 => 10:00-11:00 | 12:00-14:00 | 14:00-16:00
Day 5 => 08:00-10:00 | 13:00-15:00
Day 6 => 80:00-09:00 | 10:00-11:00

Class B Time:

Day 1 => 10:00-11:00 | 17:00-19:00
Day 2 => 08:00-09:00 | 11:00-12:00
Day 3 => 08:00-09:00 | 17:00-18:00 | 20:00-21:00
Day 4 => 08:00-09:00 | 14:00-16:00 | 17:00-18:00
Day 5 => 08:00-10:00 | 14:00-15:00
Day 6 => 10:00-11:00

How to check the two class time conflict


Solution

  • Because overlapping periods may not be a simple == comparison, class periods must be split into start and end chunks and individually processed. Unfortunately, this makes for a loop-heavy block of code -- but it is unavoidable.

    array_walk_recursive() is used to prepare your input data. The function, by design, only iterates the "leaf-nodes" of $sched -- this means that $v will never be an array, it will always be a time-span string. The function is also "depth-ignorant", so the only way properly modify the original $sched data is to make $v modifiable by reference (see: &$v).

    Once the data is restructured, it is time to get loopy. A series of 3 successive loops traverse the "targeted" Class and Day subarrays for comparison. A further 2 loops are used to access the same Day subarrays (periods) of all other Classes. Earlier code builds implemented array_walk_recursive() again to reduce total loops, but this is not best practice because using a foreach loop with an early exit with break(2) saves iterations and improves efficiency.

    Assuming that some classes may not have a day with period, I have baked in a Day check to ensure that my method doesn't cause Notices/Warnings based on missing Day subarrays.

    Code: (Demo)

    $sched=[
        'Class A'=>[
            'Day 1'=>['10:00-11:00','11:00-12:00','13:00-15:00','20:00-21:00'],
            'Day 2'=>['12:00-13:00','14:00-15:00'],
            'Day 3'=>['08:00-09:00','16:00-17:00','18:00-19:00'],
            'Day 4'=>['10:00-11:00','12:00-14:00','14:00-16:00'],
            'Day 5'=>['08:00-10:00','13:00-15:00'],
            'Day 6'=>['08:00-09:00','10:00-11:00']
        ],
        'Class B'=>[
            'Day 1'=>['09:30-10:30','17:00-19:00'],
            'Day 2'=>['08:00-09:00','11:00-12:00'],
            'Day 3'=>['08:00-09:00','17:00-18:00','20:00-21:00'],
            'Day 4'=>['08:00-09:00','14:00-16:00','17:00-18:00'],
            'Day 5'=>['08:00-10:00','14:00-15:00'],
            'Day 6'=>['10:00-11:00']
        ],
        'Class C'=>[
            'Day 1'=>['12:00-14:00']
        ]
    ];
    // Preparation: Split the dashed time spans into chunked subarrays containing start and end times
    // e.g. $sched['Class A']['Day 1'][['10:00','11:00'],['17:00','19:00']]
    array_walk_recursive($sched,function(&$v){$v=explode('-',$v);});
    
    // Comparisons:
    foreach($sched as $class=>$days){
        $other_classes=array_diff_key($sched,[$class=>'']);  // same array without target class
        foreach($days as $day=>$spans){
            //echo "\n\n$class, $day";
            foreach($spans as $period=>$span){
                //echo "\nchecking {$span[0]} & {$span[1]}";
                foreach($other_classes as $other_class=>$other_days){
                    if(isset($other_days[$day])){  // this avoids Notices/Warning about non-existent day in class
                        foreach($other_days[$day] as $other_period=>$other_span){  // compare other classes on same day
                            //echo "\nversus {$other_span[0]} & {$other_span[1]}";
                            if(($span[0]>=$other_span[0] && $span[0]<$other_span[1]) || ($span[1]<=$other_span[1] && $span[1]>$other_span[0])){
                                $result[]="Clash: $class,$day#".++$period.": {$span[0]}-{$span[1]} vs $other_class,$day#".++$other_period.": {$other_span[0]}-{$other_span[1]}";
                                break(2);  // no need to further check this $span for clashes
                            }
                        }
                    }
                }
            }
        }
    }
    echo implode("\n",$result);