根据已选择的日期/时间从数组中获取最接近的日期/时间


Get closest date/time from array based on already chosen date/times

使用一个已经选择的日期:测试示例

$dates[] = array("date" => "2016-02-18 02:00:00", "duration" => "600"); // 10 mins
$dates[] = array("date" => "2016-02-18 02:05:00", "duration" => "300"); // 5 mins
$dates[] = array("date" => "2016-02-18 02:10:00", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:25:00", "duration" => "300");
$dates[] = array("date" => "2016-02-18 02:30:00", "duration" => "600");
$alreadyChosenDates[] = array("date" => "2016-02-18 02:10:30", "duration" => "600"); // 10 mins
//$alreadyChosenDates[] = array("date" => "2016-02-18 02:05:00", "duration" => "300"); // 5 mins
function returnClosestTime($alreadyChosenDates, $dates){
    // Set an array called $closestTime that has the time difference and the key
    $closestTime = [null, null];
    // Check each element in array
    foreach($dates as $key => $date){
        foreach($alreadyChosenDates as $chosenDates){
            // Calculate difference between already chosen dates array and the dates array
            $diff = (strtotime($chosenDates["date"]) + $chosenDates["duration"]) - strtotime($date["date"]);
            if($diff < 0) $diff = $diff * -1; 
            // If $closestTime is empty, populate it
            if($closestTime[0] === null) $closestTime = [$diff, $key];
            // If $closestTime isn't empty and the current date's time difference
            // is smaller, populate $closestTime with the time difference and key
            else if($diff < $closestTime[0]) $closestTime = [$diff, $key];
        }
    }
    return $dates[$closestTime[1]];
}
echo "<pre>";
    print_r(returnClosestTime($alreadyChosenDates, $dates));
echo "</pre>";
输出:

Array
(
    [date] => 2016-02-18 02:25:00
    [duration] => 300
)

在上面的例子中,我只使用一个已经选择的日期,但我正在寻找一些帮助,我如何使用两个已经选择的日期?

所以不只是这样:

$alreadyChosenDates[] = array("date" => "2016-02-18 02:10:30", "duration" => "600"); // 10 mins

应该是这个

$alreadyChosenDates[] = array("date" => "2016-02-18 02:10:30", "duration" => "600"); // 10 mins
$alreadyChosenDates[] = array("date" => "2016-02-18 02:05:00", "duration" => "300"); // 5 mins

和最接近的日期/时间将最接近2016-02-18 02:10:302016-02-18 02:05:00,但如果最接近的时间将在2016-02-18 02:10:302016-02-18 02:05:00的中间也会很好。

我该怎么做呢?

我不知道您是否喜欢OOP方法,但是您可以使用这个类:

class ClosestIndicationFinder
{
    protected $indications;
    protected $chosenIndications;
    public function __construct(
        array $indications,
        array $chosenIndications = null
    ) {
        $this->indications = $indications;
        $this->chosenIndications = $chosenIndications;
    }
    public function setChosenIndications(array $chosenIndications)
    {
        $this->chosenIndications = $chosenIndications;
        return $this;
    }
    public function addChosenIndication($indication)
    {
        $this->chosenIndications[] = $indication;
        return $this;
    }
    public function findClosestIndication()
    {
        $this->findAverageChosenIndicationTimestamp();
        $this->findAbsoluteIndicationDifferences();
        return $this->findClosestsIndicationBasedOnDiffs();
    }
    protected $averageChosenTimestamp;
    protected function findAverageChosenIndicationTimestamp()
    {
        $sum = array_reduce(
            $this->chosenIndications,
            function ($sum, $indication) {
                return $sum + $this->calculateIndicationEndTimestamp($indication);
            },
            0
        );
        $this->averageChosenTimestamp = (int) $sum / count($this->chosenIndications);
    }
    protected $diffs;
    protected function findAbsoluteIndicationDifferences()
    {
        $this->diffs = array_map(function ($indication) {
            return abs($this->calculateIndicationsDifference(
                $indication,
                $this->averageChosenTimestamp
            ));
        }, $this->indications);
    }
    protected function calculateIndicationsDifference($indication1, $indication2)
    {
        $timestamp1 = is_array($indication1) 
            ? $this->calculateIndicationBeginningTimestamp($indication1)
            : $indication1;
        $timestamp2 = is_array($indication2) 
            ? $this->calculateIndicationEndTimestamp($indication2) 
            : $indication2;
        return $timestamp1 - $timestamp2;
    }
    protected function calculateIndicationBeginningTimestamp(array $indication)
    {
        return strtotime($indication['date']);
    }
    protected function calculateIndicationEndTimestamp(array $indication)
    {
        return strtotime($indication['date']) + $indication['duration'];
    }
    protected function findClosestsIndicationBasedOnDiffs()
    {
        $closestIndicationIndex = array_search(min($this->diffs), $this->diffs);
        if (!isset($this->indications[$closestIndicationIndex])) {
            return null;
        }
        return $this->indications[$closestIndicationIndex];
    }
}

我尝试让方法自描述,所以代码中没有注释。 OOP方法可以通过扩展类和只重写那些行为应该改变的方法来调整逻辑。例如,我看到你的另一个问题,你想要添加一个新选择的日期到选定的数组。在类中也有一个方法,但是,我将从初始日期数组中删除所选日期。但是由于我只知道最终任务,所以我很难使解决方案与初始任务紧密匹配。

对于多次选择的时间。你已经写了,但是看看最近的时间是否在中间也会很好。如果我错了,请纠正我,但是,如果你想要的日期,位于最接近所选日期的两个日期的中间,你可以计算所选日期的平均值并减少任务,只找到一个最接近的日期。

再说一遍,如果你能知道你在做什么,解决方案就能更准确。