2017-07-17 9 views
-1

私はスタッフの時間のリストを持っています。 Andreas' helpスタッフスケジュールが一人で作業する分

| 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"); 
+0

@mickmackusa日付を追加することは問題ではない、私が困惑している計算です。 – Basit

+0

@mickmackusa私は彼の2つの質問を理解した方法は、出力は誰が持っているかを示すものでなければなりません一人で、どれくらい長い間。とにかくあなたが望むものにあなたの出力を変えることができるように思われるので、あなたがそれを提示する方法はおそらくあまり重要ではありません。私は間違っているかもしれませんが、それが私がBasitを理解した方法です。また、彼はPHPで多くのqustionsと答えを持っているので、彼はあなたが彼に与えるすべての出力を処理することができると思います。 – Andreas

+0

お待ちしております... – Basit

答えて

1

私は自分の答えを完全に書き直しているので、それは明確で適切な順序で流れます。私は前の方法から少し細かい改良を加えましたが、劇的なものはありませんでした。

まず、データ準備コードです。私は、従業員IDをキーとして維持しながら、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] 
    ]; 
*/ 

これはデータ処理スニペットです。私はショートカットを構築しています。ある従業員が別の従業員と同じシフトを共有する場合、最初の従業員はただちに(明らかに)0分とみなされます。それ以外の場合は、従業員のシフトを1人ずつ他の従業員のシフトと比較して、何分だけ単独であるかを判断します。

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 
) 

あなたは1260660からの完全なシフトで1始まりwhittle()

  • スタッフ#の内部で起こっているものをフォローするのに役立ちます。 ($pieces_of_shiftは、2つの要素を持つただ一つのサブアレイを有するアレイである - 分と終了分を開始) - 開始時にのみタイムスタッフ#2と比較された後
    $pieces_of_shift=[[660,1260]];
  • 、オリジナル$pieces_of_shiftサブアレイは、二つの新たなサブアレイに置き換えられシフトの終わりの単独の時間:660から780900から1260の間にある。
    $pieces_of_shift=[[660,780],[900,1260]];
  • 次に、スタッフ#3のシフトを、スタッフ#1の2つの残りの2つの時間範囲と比較します。スタッフ#3のシフトは、最初のサブアレイの一部と重複しませんが、2番目のサブアレイでは行いません。これは、シフト時間のオーバーラップを効果的に「パンチアウト」するために、第2の時間範囲が置き換えられることを意味します。 7806601020900、および12601140
    $pieces_of_shift=[[660,780],[900,1020],[1140,1260]];
  • これはスタッフ#1のシフトの結果は、 "一人で" 時間の3つの期間を持っています。これらの3つの時間範囲(それぞれ2時間)は、合計6時間のソロワークまたは360分をもたらします。

ここはa demo with additional commentsです。


高い確率または特定のバッチにおける重複シフトの高いボリュームが存在する場合、whittle()の内部総反復は最初foreach()ループの前$colleague_shifts=array_map('unserialize', array_unique(array_map('serialize', $shifts)))を書き込むことによって低減することができます。そのことについては

、同じ多機能アプローチforeach($shifts...)の呼び出し前に、いくつかの重複したシフトをショートカットするために使用することができ、それは畳み込み価値はないかもしれないので、私はそのアプローチを実装しないことを選択しました。

+0

私はあなたの "時"を理解していないか分かりません。もし私がそれらを「正常」に戻すなら、これは正しいでしょうか? https://3v4l.org/f0s9uコードの最初の行が正しいことを確認するだけです。そして27 = 03:00翌日。 – Andreas

+0

OPから非常に静かです。私は答えが間違っているかどうかも疑問に思います。どちらもうまくいくようです。パフォーマンスの差はあまりないと思います。それは私が見ることができる限り出力の問題です – Andreas

+0

@Andreas申し訳ありませんが、過去2日間の旅行だった..今日の両方をテストし、小さな機能や何かに簡単に故障することができるものを実装するつもり。それのためのphpunitテスト。しかし、関係なく...本当に助けていただきありがとうございます。 – Basit

1

これが完成しました。

時間がかかりましたが、解決策が見つかりました。
mickmacksテストケースの解決策を見つけるために管理しました。
ここに10人の場合があります。

<?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

を参照してくださいそれは私がダミーを必要と把握することが私に長い時間がかかりました。ダミーが役に立つのは誰が知っていたのですか?

+0

そして、アレイに余分なアイテムがある理由は、私が同時に2人の人が出発しているからです。ですから、1つは一人ですが、次の人が同時に始まるので、継続時間はゼロです。 – Andreas

+0

どういうわけか、この単純なことは笑いをするのが非常に難しくなりました。 – Basit

+0

@mickmackusaうわー...それは奇妙です。それはforループが1回多く実行するようです。私が$ times [$ i]をエコーすると、それは空のエコーになります。もし私がループを早く止めれば、それはあなたの事例で働いたが、5人ではなかった。だから何か変わったことが起こっていた。私はちょうどif($ times [$ i])を追加しました。それは問題を抱えているようです。それを指摘してくれてありがとう! :-) – Andreas

関連する問題