2

I'm looking to process this array of dates and an essentially boolean single value:

Array (
    [20120401] => 1
    [20120402] => 1
    [20120403] => 1
    [20120404] => 0
    [20120405] => 0
    [20120406] => 0
    [20120407] => 1
    [20120408] => 1
    [20120409] => 1
)

Into this:

Array (
    Array(
        'start_date' => '20120401',
        'end_date' => '20120403',
        'status' => '1'
        ),
    Array(
        'start_date' => '20120404',
        'end_date' => '20120406',
        'status' => '0'
        ),
    Array('start_date' => '20120407',
          'end_date' => '20120409',
          'status' => '1'
        ),
)

So essentially splitting the array when the value changes and creating a new array with the start key and end key of where the value changed and the value of that section. Sorry if the explanation doesn't make as much sense as the example arrays!

It seems on the face of it to me perhaps a recursive function would suit whereby the 1st example arrays values get removed when processed and the function is called again with the shortened array. I could be missing something simple with this though!

Thanks!

EDIT:

Just to update this, I should have made it clearer in the question that the original array will span month divisions so I updated the code provided by hakre to take account of this. I also modified it to use the desired output array keys. In my case I do have control of the input array so can validate the dates before being passed. Thanks again hakre.

//set the time period in seconds to compare, daily in this case
$time_period = (60 * 60 * 24);

$output = array();
$current = array();
foreach($input as $date => $state) {
    //convert date to UTC
    $date = strtotime($date);

    if (!$current || $current['state'] != $state ||
        $current['to'] != $date - $time_period) {
        unset($current);
        $current = array('state' => $state, 'from' => $date, 'to' => $date);
        $output[] = &$current;
        continue;
    }
    $current['to'] = $date;
}
unset($current);

// convert date back to the desired format
foreach ( $output as $index => $section ) {
    $output[$index]['from'] = date("Ymd", $output[$index]['from'] );
    $output[$index]['to'] = date("Ymd", $output[$index]['to'] );
}
2
  • 3
    And where did you have trouble? Commented Mar 19, 2012 at 14:19
  • Sorry for the lack of what I'd tried, it was too embarrassing to post. @Shiplu point taken. Commented Mar 19, 2012 at 15:58

2 Answers 2

2

You can do this with just one loop through the array: create a variable that stores the previous key (basically stores the current key at the end of the loop's cycle) and then compare it to the new key.

Something like this:

foreach ($array as $key => $value)
{
   if ($key != $prev_key)
      // append relevant value to the new array here

   $prev_key = $key;
}
Sign up to request clarification or add additional context in comments.

Comments

0

There is no need for recursion because you can solve this iteratively. You build the current element unless the conditions are matched to create a new element. The trick is to make the first current element invalid per default, so the first entry will create a new one:

$output = array();
$current = array();
foreach($input as $date => $status) {
    if (!$current || $current[2] != $status || $current[1] != $date-1) {
        unset($current);
        $current = array($date, $date, $status);
        $output[] = &$current;
        continue;
    }
    $current[1] = $date;
}
unset($current);

Which will give you the following result for $output:

Array
(
    [0] => Array
        (
            [0] => 20120401
            [1] => 20120403
            [2] => 1
        )

    [1] => Array
        (
            [0] => 20120404
            [1] => 20120406
            [2] => 0
        )

    [2] => Array
        (
            [0] => 20120407
            [1] => 20120409
            [2] => 1
        )

)

Apart from the named keys which are numbered here, this is what you're looking for.

3 Comments

Perfect, thanks. Deceptively simple! I guess I should use a DateTime object to make the $date-1 part more robust around month ends/beginnings.
@user1278681: Depends how safe the input is. If it's not under contract/your influence, it might be wise to add some pre-cautions like sort the input array based on the value in key and / or validating the date values. Keep in mind that array keys in PHP if they are numerical will be int, not string.
Thanks, I modified your code and added it to the question to take account of month divisions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.