[SOLVED] PHP Search Array Speed

Issue

I would like to apologize in advance for the long post but I wanted to give you a clear picture of my problem. Any help is appreciated.

I have an array called $data with the following format:

enter image description here


Given an arbitrary start date, I need to search the array for matching dates:

//$holidays is an array with dates of public holidays which are not considered business days
$search_results = array();
$minDate = -10;
$maxDate = 100;
$start_date = "2015-02-25";

echo "Before loop: " . xdebug_time_index() . "<br>";

for ($i=$minDate; $i<=$maxDate; $i++) {
    if (in_array_r(getBusinessDay(new DateTime($start_date), $holidays, $i), $data)){
        $a_date = getBusinessDay(new DateTime($start_date), $holidays, $i);
        $a_key = array_search($a_date, array_column($data, "date"));
        $search_results[]=array($i, $data[$a_key]["data"]);
    }
}

echo "After loop: " . xdebug_time_index() . "<br>";

var_dump($search_results);

However, this code snippet, which might run 10-15 times as the page loads, takes a long time to execute (at least 6 seconds on bigger arrays) each turn:

enter image description here

Could you please help me understand which part of code is causing this delay and how could I possibly speed up this process?

Thank you in advance for your help.

Here are the functions used in the code snippet:

function getBusinessDay($startdate, $holidays, $days) {

    $calculator = new BusinessDaysCalculator($startdate, $holidays, [BusinessDaysCalculator::SATURDAY, BusinessDaysCalculator::SUNDAY]);
    $calculator->addBusinessDays($days);
    $result = $calculator->getDate()->format('Y-m-d');
    unset($calculator);

    return $result;
}

function in_array_r($needle, $haystack, $strict = false) {
    foreach ($haystack as $item) {
        if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && in_array_r($needle, $item, $strict))) {
            return true;
        }
    }
    return false;
}

And the calculator which returns the next business day (skips weekends and any dates in the $holidays array):

class BusinessDaysCalculator {

    const MONDAY    = 1;
    const TUESDAY   = 2;
    const WEDNESDAY = 3;
    const THURSDAY  = 4;
    const FRIDAY    = 5;
    const SATURDAY  = 6;
    const SUNDAY    = 7;

    /**
     * @param DateTime   $startDate       Date to start calculations from
     * @param DateTime[] $holidays        Array of holidays, holidays are no considered business days.
     * @param int[]      $nonBusinessDays Array of days of the week which are not business days.
     */
    public function __construct(DateTime $startDate, array $holidays, array $nonBusinessDays) {
        $this->date = $startDate;
        $this->holidays = $holidays;
        $this->nonBusinessDays = $nonBusinessDays;
    }

    public function addBusinessDays($howManyDays) {
        $i = 0;
        while ($i < abs($howManyDays)) {
            if ($howManyDays < 0) {
                $this->date->modify("-1 day");
            } else {
                $this->date->modify("+1 day");
            }
            if ($this->isBusinessDay($this->date)) {
                $i++;
            }
        }
    }

    public function getDate() {
        return $this->date;
    }

    private function isBusinessDay(DateTime $date) {
        if (in_array((int)$date->format('N'), $this->nonBusinessDays)) {
            return false; //Date is a nonBusinessDay.
        }
        foreach ($this->holidays as $day) {
            if ($date->format('Y-m-d') == $day->format('Y-m-d')) {
                return false; //Date is a holiday.
            }
        }
        return true; //Date is a business day.
    }
}

UPDATE 1:
I updated the structure of the $data array to

enter image description here

and the loop to:

for ($i=$minDate; $i <= $maxDate; $i++) {
        $day = getBusinessDay(new DateTime($start_date), $holidays, $i);
        if (array_key_exists($day, $data)) {
            $search_results[]=array($i, $data[$day]);
        }
}

The times improved only slightly:

enter image description here

Is array_key_exists the cause of the delay?

UPDATE 2:
This is the $holidays array (It’s static, always the same):

enter image description here

Solution

Accoring to your comment, the date column contains unique values.
Since you’re only filtering on the date column, it’s a lot more efficient to index the array via the date column, so you should restructure your data like this:

$data = array(
    '2015-02-19' => 1.35625,
    '2015-02-20' => 1.4015,
    '2015-02-23' => 0.9095,
    '2015-02-24' => 1.0635,
    '2015-02-25' => 1.08775,
    '2015-02-26' => 0.947,
    /* ... */
)

in_array needs to loop through the whole array an element corresponding to one date, which can be slow for large arrays.
Using this structure, you can get the data immediately by accessing the data with $data[$date].

Answered By – Benoit Esnard

Answer Checked By – Mary Flores (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *