У меня есть список раз для персонала. Мне нужно выяснить, работает ли кто-нибудь из сотрудников один и сколько минут они работают один в течение дня
| staff| start | end | |:--- |:--- |:--- | | 1 | 11:05 | 20:00 | | 2 | 11:00 | 17:00 | | 3 | 19:00 | 03:00 | | 4 | 13:00 | 20:00 | | 5 | 19:00 | 03:00 |
С помощью Андреаса следующий код, который получает первый и последний человек, который работал один с одинокими минутами, но это не совсем правильно. Потому что, если бы было 3 человека с разным временем, которые работали в одиночку, это создаст проблему. https://3v4l.org/6OmjO
$staff = array(1,2,3,4,5); $start = array("11:05", "11:00", "19:00", "13:00", "19:00"); $end = array("20:00", "17:00", "03:00", "20:00", "03:05"); array_multisort($start, $end, $staff); $aloneStart = (strtotime($start[1]) - strtotime($start[0])) / 60; // first and second items are the ones that may be working alone at start $aloneEnd = (strtotime($end[count($end) - 1]) - strtotime($end[count($end) - 2])) / 60; // last and second to last are the ones that may be working alone at end if ($aloneStart > 0) { $staffAloneStart = $staff[0]; //must be the first who worked alone echo "minutes alone at start: " . $aloneStart . " and it was " . $staffAloneStart . "\n"; } if ($aloneEnd > 0) { $staffAloneEnd = $staff[count($end) - 1]; // must be the last to end that worked alone echo "minutes alone at end: " . $aloneEnd . " and it was " . $staffAloneEnd . "\n"; } $aloneTime = intval($aloneStart) + intval($aloneEnd); echo "total time alone " . $aloneTime;
с последующим массивом, вы увидите, что минуты для первого пользователя должны быть более 5 минут, потому что он работает один больше вечером.
$staff = array(1, 2, 3, 4, 5); $start = array("11:05", "11:10", "19:00", "13:00", "19:00"); $end = array("20:00", "17:00", "03:00", "16:00", "03:00");
Я полностью переписываю свой ответ, чтобы он был ясен и протекал в правильном порядке. Я сделал несколько незначительных усовершенствований из своего предыдущего метода, но ничего резкого.
Во-первых, это код подготовки данных. Я конвертирую значения времени входов и выходов OP в hh:mm
в простые минутные значения, сохраняя идентификаторы сотрудников как ключи.
// My test data in OP's format to start with: $staff=[1,2,3]; $start=['11:00','13:00','17:00']; $end=['21:00','15:00','19:00']; // My data preparation method: foreach($staff as $i=>$v){ $on=explode(':',$start[$i]); // separate hh from mm of start of shift $on_minutes=$on[0]*60+$on[1]; // calculate total minutes from start of day $off=explode(':',$end[$i]); // separate hh from mm of end of shift $off_minutes=($off[0]+($on[0]>$off[0]?24:0))*60+$off[1]; // calculate minutes from start of day, factoring shift that run past midnight $shifts[$v]=[$on_minutes,$off_minutes]; // store prepared data for future processes } /* (new prepared array): $shifts=[ 1=>[660,1260], 2=>[780,900], 3=>[1020,1140] ]; */
Это фрагмент обработки данных. Я построил ярлык – если один сотрудник разделяет идентичный сдвиг с другим сотрудником, то первый сотрудник сразу же считается, что имеет только нулевые минуты (очевидно). В противном случае сдвиг сотрудника сравнивается один за другим с изменениями других сотрудников, чтобы определить, сколько минут они остались в одиночестве.
function whittle($colleague_shifts,$pieces_of_shift){ // initially, PoS is only one element foreach($colleague_shifts as $k=>$cs){ foreach($pieces_of_shift as $i=>$ps){ if($cs[0]<=$ps[0] && $cs[1]>=$ps[1]){ unset($pieces_of_shift[$i]); continue; // fully covered by coworker } $temp=[]; if($ps[0]<$cs[0] && $cs[0]<$ps[1]){ $temp[]=[$ps[0],$cs[0]]; // push new unmatched start into temp PoS array } if($ps[1]>$cs[1] && $cs[1]>$ps[0]){ $temp[]=[$cs[1],$ps[1]]; // push new unmatched end into temp PoS array } if($temp){ array_splice($pieces_of_shift,$i,1,$temp); // replace the current PoS with 1 or 2 new PoS subarrays } } if(!$pieces_of_shift){ return 0; // no minutes alone } } // subtract all end alone minutes from all start alone minutes return array_sum(array_column($pieces_of_shift,1))-array_sum(array_column($pieces_of_shift,0)); } foreach($shifts as $id=>$s){ $colleague_shifts=array_diff_key($shifts,[$id=>'']); // generate array excluding target worker's shift if(in_array($s,$colleague_shifts)){ // check for same start and end times elsewhere $alone[$id]=0; // exact duplicate allows shortcut as "never alone" }else{ $alone[$id]=whittle($colleague_shifts,[$s]); // whittle down times where target employee is alone } } var_export($alone);
сfunction whittle($colleague_shifts,$pieces_of_shift){ // initially, PoS is only one element foreach($colleague_shifts as $k=>$cs){ foreach($pieces_of_shift as $i=>$ps){ if($cs[0]<=$ps[0] && $cs[1]>=$ps[1]){ unset($pieces_of_shift[$i]); continue; // fully covered by coworker } $temp=[]; if($ps[0]<$cs[0] && $cs[0]<$ps[1]){ $temp[]=[$ps[0],$cs[0]]; // push new unmatched start into temp PoS array } if($ps[1]>$cs[1] && $cs[1]>$ps[0]){ $temp[]=[$cs[1],$ps[1]]; // push new unmatched end into temp PoS array } if($temp){ array_splice($pieces_of_shift,$i,1,$temp); // replace the current PoS with 1 or 2 new PoS subarrays } } if(!$pieces_of_shift){ return 0; // no minutes alone } } // subtract all end alone minutes from all start alone minutes return array_sum(array_column($pieces_of_shift,1))-array_sum(array_column($pieces_of_shift,0)); } foreach($shifts as $id=>$s){ $colleague_shifts=array_diff_key($shifts,[$id=>'']); // generate array excluding target worker's shift if(in_array($s,$colleague_shifts)){ // check for same start and end times elsewhere $alone[$id]=0; // exact duplicate allows shortcut as "never alone" }else{ $alone[$id]=whittle($colleague_shifts,[$s]); // whittle down times where target employee is alone } } var_export($alone);
Вывод:
array ( 1 => 360, // alone from 11am-1pm, 3pm-5pm, and 7pm-9pm 2 => 0, // never alone 3 => 0, // never alone )
Чтобы помочь вам следить за тем, что происходит внутри whittle()
660
до 1260
. ( $pieces_of_shift
– массив с одним подмассивом с двумя элементами – начальные минуты и конечные минуты) $pieces_of_shift=[[660,1260]];
$pieces_of_shift
заменяется двумя новыми подмассивами – единственным временем в начале смены и единственным временем в конце смены: с 660
по 780
и с 900
до 1260
. $pieces_of_shift=[[660,780],[900,1260]];
$pieces_of_shift=[[660,780],[900,1020],[1140,1260]];
660
до 780
, от 900
до 1020
и от 1140
до 1260
. Эти 3 диапазона одного времени (по 2 часа каждый) дают 6 часов общей сольной работы или 360 минут. Вот демонстрация с дополнительными комментариями .
Если существует высокая вероятность или большой объем повторяющихся сдвигов в определенной партии, то общие итерации внутри whittle()
могут быть уменьшены путем записи $colleague_shifts=array_map('unserialize', array_unique(array_map('serialize', $shifts)))
перед первым циклом foreach()
.
В этом случае один и тот же многофункциональный подход может использоваться для сокращения нескольких повторяющихся сдвигов перед вызовом foreach($shifts...)
, но я решил не применять этот подход, потому что это может не стоить свертки.
Понял!
Потребовалось некоторое время, но я нашел решение.
Удалось найти решение для теста mickmacks.
Вот дело десять человек и, похоже, задерживается для этого.
<?php $staff = array(1,2,3,4,5,6,7,8,9,10); $start = array("11:00", "13:00", "17:00", "17:00", "11:00", "13:30", "16:50", "18:30","17:00", "11:00"); $end = array("21:00", "15:00", "19:00", "19:30", "11:30", "15:10", "18:45", "19:45", "19:00", "11:30"); // Add staff number to end of time ex 11:00 => 11:00#2 For($i=0; $i<count($start);$i++){ $start[$i] .= "#" . $staff[$i]; $end[$i] .= "#" . $staff[$i]; } $t = array_merge($start,$end); // create one long array with all in and out times sort($t); //var_dump($t); // Multisport is needed to get all arrays in time order as reference array_multisort($start, $end, $staff); // Find first start time (11:00) and slice array thwre, build string $test = implode(PHP_EOL,array_slice($t, array_search($start[0], $t))); // Find the times before first start (night end times) and add them last in string $test .= PHP_EOL . implode(PHP_EOL,array_slice($t, 0,array_search($start[0], $t))); $times = explode(PHP_EOL, $test); // explode to make it array again // Var_dump($times); $WhoIsInDaHouse = array("dummy"); // add a dummy variable since 0=false in later if $j=0; for($i=0; $i<count($times);$i++){ //echo $times[$i] ." " . $i ."\n"; if($times[$i]){ $TimePerson = explode("#", $times[$i]); $Time = $TimePerson[0]; $person = $TimePerson[1]; $inout = array_search($person, $WhoIsInDaHouse); //is person in house and about to leave? If($inout != false){ //if person enter work false, if true: key of person leaving in $WhoIsInDaHouse //Here $person is leaving work Unset($WhoIsInDaHouse[$inout]); If(count($WhoIsInDaHouse) == 2){ // someone will now be alone since we have a dummy $Alone[$j]["start"] = $Time; $Alone[$j]["who"] = array_slice($WhoIsInDaHouse, -1)[0]; }elseif(count($WhoIsInDaHouse) == 1 && $prevcount == 2){ // Only dummy left $Alone[$j]["end"] = $Time; $Alone[$j]["duration"] = strtotime($Alone[$j]["end"])-strtotime($Alone[$j]["start"]); $j++; } }Else{ // Here person enters work $WhoIsInDaHouse[] = $person; If(count($WhoIsInDaHouse) == 2){ // someone is entering alone $Alone[$j]["start"] = $Time; $Alone[$j]["who"] = $person; }elseif(count($WhoIsInDaHouse)>2 && $prevcount == 2){ // not alone anymore $Alone[$j]["end"] = $Time; $Alone[$j]["duration"] = strtotime($Alone[$j]["end"])-strtotime($Alone[$j]["start"]); $j++; } } $prevcount = count($WhoIsInDaHouse); } } foreach($Alone as $key => &$loner){ if($loner["duration"]==0) unset($Alone[$key]); } Var_dump($Alone);
И посмотрите, как работает красота https://3v4l.org/bT2bZ
Мне потребовалось много времени, чтобы понять, что мне нужен манекен. Кто знал, что манекен может быть полезен?