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');
    }
}
Community
  • 1
  • 1
zen
  • 970
  • 2
  • 23
  • 52

5 Answers5

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)

nice ass
  • 15,874
  • 7
  • 43
  • 79
  • 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`? – akirk Mar 21 '14 at 21:11
  • You are right. `$times = array_intersect($times, $array1);` would prevent that – nice ass Mar 21 '14 at 21:16
  • 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. – akirk Mar 21 '14 at 21:23
  • How do these array_map, array_combine, and array_fill_keys methods compare to foreach loops speed-wise? – zen Mar 21 '14 at 21:51
  • 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 – nice ass Mar 21 '14 at 21:56
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);
}
akirk
  • 6,388
  • 2
  • 31
  • 54
  • I like this one the most so far and it works perfectly. – zen Mar 21 '14 at 21:50
  • 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? – zen Mar 21 '14 at 21:52
  • 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. – akirk Mar 21 '14 at 21:55
  • 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. – zen Mar 21 '14 at 22:01
  • 1
    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. – akirk Mar 21 '14 at 22:04
  • You are right. Either way, it seems to be speedy when I test it so I'll just give it a go and optimize it if I run into performance issues. Thanks again! – zen Mar 21 '14 at 22:08
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'];
    }
}
larsAnders
  • 3,787
  • 1
  • 12
  • 19
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'];
    }
}
Crisp
  • 11,191
  • 3
  • 36
  • 41
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;
    }
}
Mike Brant
  • 66,858
  • 9
  • 86
  • 97