0

I have 2 arrays I want to compare and then create a third array with those properties. I have first array (array1) which just includes top of hour time values such as:

Array ( 
[0] => 1393326000 
[1] => 1393329600 
[2] => 1393333200 
[3] => 1393336800 
[4] => 1393340400 
[5] => 1393344000 
[6] => 1393347600 
[7] => 1393351200 
[8] => 1393354800 
[9] => 1393358400 
[10] => 1393362000 
[11] => 1393365600 
[12] => 1393369200 
)

Then I have the second data array that has the data I need, it looks like this, except has thousands of values. (array2)

Array (
[0] => Array ( [time] => 1393328145 [output] => 431 ) 
[1] => Array ( [time] => 1393328146 [output] => 123 ) 
[2] => Array ( [time] => 1393354800 [output] => 543 ) 
)

So I am essentially trying to get a third array that has same keys as array #1, but fill in output field with what's in array 2, except if it's not in array #2, then just fill it as '0'.

So my final desired result is an array like this:

Array ( 
[1393326000] => array ( [output] => 0 ) 
[1393329600] => array ( [output] => 0 ) 
[1393333200] => array ( [output] => 0 ) 
[1393336800] => array ( [output] => 0 ) 
[1393340400] => array ( [output] => 0 ) 
[1393344000] => array ( [output] => 0 )
[1393347600] => array ( [output] => 0 ) 
[1393351200] => array ( [output] => 0 ) 
[1393354800] => array ( [output] => 543 ) 
[1393358400] => array ( [output] => 0 ) 
[1393362000] => array ( [output] => 0 ) 
[1393365600] => array ( [output] => 0 ) 
[1393369200] => array ( [output] => 0 ) 
)

Basically all values are 0 except the ones that match from data table, in this case 1393354800

I've tried running a loop on the first array then using this helpful recursive in_array function, but I'm not sure how to get the array values of the same array it finds it in.

// helpful function
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;
}

// do our thing
$newArray = array();
$totalHours = count($array1);

for ($i = 0; $i < $totalHours; $i++) 
{
    $currentHour = $array1[$i];
    if (in_array_r($currentHour, $array2))
    {
        $newArray[$currentHour] = array('output' => ); // Get output of current array index?
    }
    else
    {
        $newArray[$currentHour] = array('output' => '0');
    }
}

5 Answers 5

3
// "time" column, same as array_column($array2, 'time') in PHP 5.5+
$times = array_map('reset', $array2);

// "output" column
$out   = array_map('end', $array2);

// combine columns, and merge them with missing keys
$final = array_combine($times, $out) + array_fill_keys(array_values($array1), 0);

You might need to sort the final array by time (ksort)

Sign up to request clarification or add additional context in comments.

5 Comments

Very elegant but doesn't this assume that $array2 only contains values that also are in $array1, so that $final could contain values that hadn't been in $array1?
You are right. $times = array_intersect($times, $array1); would prevent that
Hm, i think you would also need to array_flip($final) and use array_intersect_key and then array_flip back. Also your output array just contains the value and not another associative array. Still it is a nice solution.
How do these array_map, array_combine, and array_fill_keys methods compare to foreach loops speed-wise?
They all loop internally, so I'm pretty sure you're better off using a foreach loop. But if you're running on PHP 5.5 you can take advantage of array_column, then assemble your final array inside a single loop where you only compare indexes
1

This should do the job (without your helpful function):

$newArray = array();
foreach ($array1 as $hour) {
    // you need to go through the whole array anyway
    foreach ($array2 as $k => $o) {
        if ($o["time"] != $hour) continue;

        // only reached if the hour matches -> add entry to $newArray
        $newArray[$hour] = array("output" => $o["output"]);

        // remove from array2 to speed up things a little:
        // it has already been found, we don't need to compare this upon the next iteration
        unset($array2[$k]);

        // this basically goes back to the top of the outer loop
        continue 2;
    }
    // this is only reached when the $array2 loop never found anything
    // so add the 0 value to the array
    $newArray[$hour] = array("output" => 0);
}

6 Comments

I like this one the most so far and it works perfectly.
The only downside is that the inner loop basically runs every time, so if I have 24 hours, it will run 24 times. This might be an issue when I have say 10,000 items in an array. Not sure if there's a way around it?
Well, it reduces the size of the array by using unset. If $array2 is sorted, you could also optimize with a second if at the top like if ($o["time"] > $hour) break;. In the best case the inner loop is only executed once for each outer loop, because it finds a match right away.
Yea I saw the unset, but I don't think it will really help since there's only going to be at most 24 hours in $array1 (sorry, should have mentioned that!). If $array2 has 10,000 items, and we remove one for each hour, we'll still have like 9,976 or so items, which probably doesn't make any noticeable different in performance.
But it only goes through the whole $array2 if the hour from $array1 is not in $array2 which sounds to be unlikely. You could probably optimize for that case but a current CPU probably won't let you wait long for 240,000 iterations.
|
1

Here's a straightforward way to do it. I edited your helper function slightly, so that if it finds the needle in the haystack, it returns that item.

// helpful function, edited slightly
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 $item;
        }
    }
    return false;
}

$newArray = array();
$totalHours = count($array1);

foreach ($array1 as $key => $value) {
    $result = in_array_r($value, $array2);
    if($result != FALSE) {
        $newArray[$value] = $result['output'];
    }
}

Comments

1

Here's a way to do it by first building array3 with the default value, then looping over array2 and adding the matching values

$emptyValues = array_fill(0, count($array1), array('output' => 0));
$array3 = array_combine(array_flip($array1), $emptyValues);

foreach ($array2 as $value) {
    if (isset($array3[$value['time']])) {
        $array3[$value['time']]['output'] = $value['output'];
    }
}

Comments

1

Give this a try:

$array_1 = ...; // your first array
$array_2 = ...; // your second array

// get Array #2 into a workable format
// build a map/array with `time` values as keys and `output` values as values
$time_to_output_map = array();
array_walk($array_2, function ($value, $key_not_used) use ($time_to_output_map) {
    $time_to_output_map[$value['time']] = $value['output'];
});

// get the values in array 1 set up as keys
$array_1_flipped = array_flip($array_1);

// merge arrays on keys
$final_array = array();
foreach($array_1_flipper as $key => $value_not_used) {
    if (array_key_exists($key, $time_to_output_map)) {
        $final_array[$key] = $time_to_output_map['$key'];
    } else {
        $final_array[$key] = 0;
    }
}

Comments

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.