获取包含已排序时隙

时间:2017-02-16 09:51:06

标签: php arrays algorithm search associative-array

给定一个包含如下时隙的PHP关联数组:

$timeSlots = Array (
    "12:00:00" => "12:05:00",
    "12:10:00" => "12:15:00",
    "16:40:00" => "16:45:00",
    "16:50:00" => "17:00:00"
);

假设所有间隔都不重叠,并且开始时间都按升序排序。

我想要一个函数返回当前时间和下一个可用时隙的可用时隙。我假设所有的插槽都是免费的 即,现在说时间是16:42,然后我想返回第3个时段 - “16:40:00”=> “16:45:00”并以“16:50:00”=>返回下一个广告位“17时00分○○秒”即可。

我尝试的是这样的,使用线性搜索来获取时间间隔:

function searchTimeSlots ($currentTime) {
    global $timeSlots;
    $timeSlot = null;
    $getNext = false;

    // get the current time slot and next available one or just next available one if there's no current time slot
    foreach ($timeSlots as $fromTime => $toTime) {
        if ($getNext) {
            $timeSlot['next'] = Array (
                "start" => $fromTime,
                "finish" => $toTime
            );
            break;
        }
        if (($currentTime >= $fromTime) && ($currentTime < $toTime)) {
            $timeSlot = Array (
                "current" => Array (
                    "start" => $fromTime,
                    "finish" => $toTime
                )
            );
            $getNext = true;
        }else if ($currentTime < $fromTime) {
            $timeSlot = Array (
                "next" => Array (
                    "start" => $fromTime,
                    "finish" => $toTime
                )
            );
            break;
        }
    }

    // if there's no current or next slot available send the first available slot
    if ($timeSlot == null) {
        foreach ($timeSlots as $fromTime => $toTime) {
            $timeSlot = Array (
                "next" => Array (
                    "start" => $fromTime,
                    "finish" => $toTime
                )
            );
            break;
        }
    }

    return $timeSlot;
}

这将返回数组

Array
(
    [current] => Array
        (
            [start] => 16:40:00
            [end] => 16:45:00
        )

    [next] => Array
        (
            [start] => 16:50:00
            [end] => 17:00:00
        )

)

我想知道是否有更好的方法来获取此数组。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:0)

希望您正在寻找以下内容:

<?php
$timeSlots = Array (
  "12:00:00" => "12:05:00",
  "12:10:00" => "12:15:00",
  "16:40:00" => "16:45:00",
  "16:50:00" => "17:00:00"
);

function getTimeSlots ($timeSlots,$currentTime) {

 $new_time_slot = [];
 array_walk($timeSlots,function($end,$start) use($currentTime,&$new_time_slot){
    if($currentTime<=strtotime($start)){
        $new_time_slot[$start] = $end;
    }
});

  return $new_time_slot;
}

$currentTime = strtotime("12:14:00"); // or time() to get current time

$new_time_slot = getTimeSlots($timeSlots,$currentTime);

print_r($new_time_slot);

答案 1 :(得分:0)

  • 给出已排序的非重叠时间列表($timeSlots

  • 给定特定时间($ timeNow)

快速搜索时间列表并返回以下内容:

  • 1)如果timeNowtimeSlot范围内,则返回timeSlot

  • 2)如果在&#39; timeNow&#39;之后有一个有效的timeSlot可用。然后返回下一个有效的timeSlot

  • 查询需要高效,因为可以重复调用不同的特定时间(&#39; timeNow&#39;)

思想

由于timeslots是一个固定的数字,有序和非有限,因此找到所需数字的最快方法是使用二进制搜索&#39;在阵列上。

要求测试“时间点”&#39;在timeSlot的持续时间范围内不是问题。

识别下一个有效timeSlot的要求意味着只有“真实时间”和“真实时间”。不能在&time;时间&#39;阵列。 &#39;差距&#39;在&#39; real timeSlots&#39;之间需要记录以便“二进制搜索”。会正常工作。即timeNow将永远是“真实时间”和“真实时间”。或者“差距”紧接在下一个实时时间之前#39;

实施

1)&#39; timeSlot&#39;的有序数组。 ($startTimes)其中每个&#39; timeSlot&#39;具有以下属性:

  • 开始时间:时间戳秒,便于比较和输出格式
  • 结束时间:时间戳秒
  • &#39;真实&#39; :一个布尔值表示一个真实的&#39;时间间隔&#39;在timeSlots之间

我决定将所有代码放在一个类(TimeRanges)中,因为要从输入到内部格式进行转换。此外,输入timeSlots只需要在startTimes转换一次。

输出

  • 一个零,一个或两个&#39; timeSlot&#39;的数组。条目。

Demonstration (with test data) at eval.in

Class: TimeRanges source

TimeRanges

class TimeRanges {

  /**
   * need this for binary search
   */     
  private $startTimes = array(); 
  private $lastSlotIdx = 0;

  // outputs so you don't need to run the search again

  private $timeNow = 0;
  private $outSlots = array();  

  private $dateWhen = null; // used to convert seconds to different formats

  /**
   * @param array The timeSlots that will be searched
   */     
  public function __construct(array $timeSlots) {
      $this->buildAllstartTimes($timeSlots);
      $this->dateWhen = new \DateTime();
  }

  /**
   * Create a list of real and 'virtual' timeslots.  
   * i.e. All gaps between the real timeslots we treat as a timeslot 
   *      when searching for a matching timeslot   
   */     
  protected function buildAllStartTimes($timeSlots) {

      $this->startTimes = array();

      $iter = new ArrayIterator($timeSlots); 

      $prvEndTime = PHP_INT_MAX; // needed to identify 'gaps'

      while ($iter->valid()) {

          $curStartTime = $this->asSeconds($iter->key());   // seconds
          $curEndTime = $this->asSeconds($iter->current()); // so is easy to check

          if ($curStartTime > $prvEndTime) { // make a 'gap' timeslot
              $this->startTimes[]  = ['s' => $prvEndTime + 1,
                                      'e' => $curStartTime,
                                      'real' => false];
              $prvEndTime = $curStartTime;

          } else { // is real
              $this->startTimes[] = ['s' => $curStartTime,
                                     'e' => $curEndTime,
                                     'real' => true];
              $prvEndTime = $curEndTime;
              $iter->next();           
          }          
      }
      $this->lastSlotIdx = count($this->startTimes) - 1;
  }

  /**
   *  Search the array using a modifield binary search
   *  
   *  This can return zero, one or two 'real' timeslots:      
   *  
   *  o timeNow before first timeslot -- return first timeslot
   *  o timeNow after last timeslot   -- return empty array
   *  o timeNow on a 'gap'            -- return one timeslot (the next one)     
   *      
   * @param string current time 
   */        
  public function findTimeSlots($timeNow) {
      $this->timeNow = $this->asSeconds($timeNow); 
      $startSlotIdx = $this->searchStartTimes($this->timeNow, 0, $this->lastSlotIdx);      


      $returnedSlots = 1;
      if ($startSlotIdx > $this->lastSlotIdx ) { 
          $returnedSlots = 0;

      } elseif (    isset($this->startTimes[$startSlotIdx])  
                 && $this->startTimes[$startSlotIdx]['real']) { 

          $returnedSlots = 2;
      } 


      $out = array();  // need current and next real timeslots in the array    
      for ($numSlots = $returnedSlots; $numSlots > 0; $numSlots--, $startSlotIdx++) {

          $startSlotIdx = $this->getNextRealTimeSlotIdx($startSlotIdx);

          if (    isset($this->startTimes[$startSlotIdx])
               && $this->startTimes[$startSlotIdx]['real']) { // valid timeslot

              $out[] = ['s' => $this->asHHMMSS($this->startTimes[$startSlotIdx]['s']),
                        'e' => $this->asHHMMSS($this->startTimes[$startSlotIdx]['e'])];
          }
      }      
      $this->outSlots = $out;
      return $out;
  }

  /**
   * find required timeslot using a nodified binary search on $startTimes 
   *      
   * This can be used to find the next valid timeslot so we need to return 
   * various conditions:
   * 
   *  o  -1           -- timeNow is before the first valid timeslot
   *  o  0 .. n       -- timeslot (real or not) that is within the array
   *  o  PHP_MAX_INT  -- timeNow is after the end of the array         
   *      
   * @param string   current time   
   * @param integer  startRange  to  timeslots to search  
   * @param integer  endRange  of timeslots  to search
   * @return integer index 
   */ 
  protected function searchStartTimes($timeNow, $rangeStart, $rangeEnd) {  

//      \Kint::dump(__METHOD__.__LINE__, $timeNow, $this->asHHMMSS($timeNow), $rangeStart, $rangeEnd);

      while ($rangeStart <= $rangeEnd) { 

          // mid point of the range
          $currentIdx = (int) ($rangeStart + ($rangeEnd - $rangeStart) / 2); // see floor()

          if ($timeNow >= $this->startTimes[$currentIdx]['s']) { // possible start key

              // check is within the end of the timeSlot
              if ($timeNow <= $this->startTimes[$currentIdx]['e']) { // found it
                  return $currentIdx; // finished
              }   

              // not found correct time slot yet - search upper range
              if ($timeNow >= $this->startTimes[$currentIdx]['s']) {
                  $rangeStart = $currentIdx + 1;                  
              }

          } else {
            // timeNow is after current StartTime  - go find a lower range
              $rangeEnd = $currentIdx - 1;                            
          } 
      } // endwhile

      return $rangeEnd < 0 ? -1 : PHP_INT_MAX; // not in the array
  }

  /**
   * Find the next 'real' timeSlot in the array
   * 
   *  The input can be: 
   *     o Before the first time in the array
   *     o On a timeSlot in the array
   *     o On a 'gap' in the array 
   *
   *  PHP_INT_MAX indicates not in the array.   
   * 
   * @param  integer Current timeslot position 
   * @return integer    
   */
  protected function getNextRealTimeSlotIdx($startSlotIdx) {

      while (    $startSlotIdx < 0  
             || ( isset($this->startTimes[$startSlotIdx])
                  && !$this->startTimes[$startSlotIdx]['real'])) {

          $startSlotIdx++;       
      }

      if (!isset($this->startTimes[$startSlotIdx])) {
          $startSlotIdx = PHP_INT_MAX;
      }

      return $startSlotIdx;
  }

  /**
   *  get any information from the class. It is maintained correctly
   */     
  public function __get($name) {
     if (property_exists($this, $name)) {
        return $this->$name;
     }
     return null;
  }

  public function asSeconds($hhmmss) {
      return strtotime($hhmmss);
  }

  public function asHHMMSS($seconds) {
      $this->dateWhen->setTimestamp($seconds);
      return $this->dateWhen->format('H:i:s');
  }

} // end class

测试数据和查找许多不同的&time; <#em>

输入实时时间段:

$timeSlots = Array (
    "12:00:00" => "12:05:00",
    "12:10:00" => "12:15:00",
    "16:40:00" => "16:45:00",
    "16:50:00" => "17:00:00"
);

示例&#39; timeNow&#39;列表:

 $testData = ['11:59:00', '12:01:00', '15:00:00', '16:51:00', '17:01:00'];

创建类并执行查找:

$timeRanges = new TimeRanges($timeSlots);

// run the search
$timeRanges = new TimeRanges($timeSlots);

$foundSlots = array();
foreach ($testData as $timeNow) { // process all the test data times...
    $foundSlots[] = $timeRanges->findTimeSlots($timeNow);
}

输出结果

testTime: 11:59:00
Array
(
    [0] => Array
        (
            [s] => 12:00:00
            [e] => 12:05:00
        )
)


testTime: 12:01:00
Array
(
    [0] => Array
        (
            [s] => 12:00:00
            [e] => 12:05:00
        )
    [1] => Array
        (
            [s] => 12:10:00
            [e] => 12:15:00
        )
)

testTime: 15:00:00
Array
(
    [0] => Array
        (
            [s] => 16:40:00
            [e] => 16:45:00
        )
)

testTime: 16:51:00
Array
(
    [0] => Array
        (
            [s] => 16:50:00
            [e] => 17:00:00
        )
)

testTime: 17:01:00
Array
(
)