Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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'] );
}
share|improve this question
3  
And where did you have trouble? –  Josh Mar 19 '12 at 14:19
 
What have you tried? –  shiplu.mokadd.im Mar 19 '12 at 14:25
 
Sorry for the lack of what I'd tried, it was too embarrassing to post. @Shiplu point taken. –  user1278681 Mar 19 '12 at 15:58
add comment

2 Answers

up vote 0 down vote accepted

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.

share|improve this answer
 
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 Mar 19 '12 at 16:14
 
@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. –  hakre Mar 19 '12 at 16:34
 
Thanks, I modified your code and added it to the question to take account of month divisions. –  user1278681 Mar 20 '12 at 15:30
add comment

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;
}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.