Merge 2 arrays in PHP, keeping only keys from first array

Viewed 2424

I have these two arrays:

$list = [
    'fruit' => [],
    'animals' => [],
    'objects' => [],
];

$dataArray = [
    'fruit' => 'apple',
    'animals' => ['dog', 'cat'],
    'asd' => 'bla'
];

I want to merge them so that $list at the end is:

[fruit] => Array
    (
        [0] => apple
    )

[animals] => Array
    (
        [0] => dog
        [1] => cat
    )

[objects] => Array
    (
    )

so, things to pay attention to:

  1. even if 'fruit' had only one element, is still an array in $list
  2. keys missing from $list ('asd' key) are simply ignored
  3. keys with no values are still kept, even if empty

Using array_merge doesn't work:

$merged = array_merge($list, $dataArray);

[fruit] => apple
[animals] => Array
    (
        [0] => dog
        [1] => cat
    )

[objects] => Array
    (
    )

[asd] => bla

I managed to get what I want with this:

foreach ($dataArray as $key => $value) {
    if (isset($list[$key])) {
        if (is_array($value)) {
            $list[$key] = $value;
        }
        else {
            $list[$key] = [$value];
        }

    }

}

But I was wondering if there was a cleaner way to do it or some other php function that I'm not aware of.

4 Answers

You can achieve this in a couple of steps:

$result = array_intersect_key($dataArray, $list);

This will return you an array containing all the elements that are in the first array, and then filter against the keys in the second. This gives you:

Array
(
  [fruit] => apple
  [animals] => Array
    (
        [0] => dog
        [1] => cat
    )
)

You can then re-add the missing keys from the first array, using:

$result + $list;

The difference between the + operator and array_merge is that the former does not overwrite the first array using the values from the second. It will just add any missing elements.

In one line, this works out as:

$result = array_intersect_key($dataArray, $list) + $list;

You can see it in action here: https://eval.in/865733

EDIT: Sorry, completely missed the part about keeping the elements as arrays. If they'll always be arrays, then you can add a quick one-liner like:

$result = array_map(function ($e) { return (array) $e; }, $result);

Which will cast all top-level elements to an array. If your logic is more complex than that, then I'm not sure you'll find a way to do it with in-built functions, sorry.

The second rule ("2. keys missing from $list ('asd' key) are simply ignored") tells me to iterate over $list, not over $dataArray. If $dataArray is much bigger than $list, iterating over it is a waste of time as most of its elements are simply ignored.

Your rules do not explain how to process the elements of $list when they are not empty (I'll assume they are always arrays, otherwise the game changes and it becomes too complicated to provide generic code to handle it).

The code I suggest looks like this:

// Build the result in $result
// (you can modify $list as well if you prefer it that way)
$result = array();

// 2. keys missing from $list ('asd' key) are simply ignored
// iterate over the keys of $list, handle only the keys present in $dataArray
foreach ($list as $key => $value) {
    if (array_key_exists($dataArray, $key)) {
        // $key is present in $dataArray, use the value from $dataArray
        $value = $dataArray[$key];

        // 1. even if 'fruit' had only one element, is still an array in $list
        if (! is_array($value)) {
            $value = array($value);
        }
    }

    // 3. keys with no values are still kept, even if empty
    // if the key is not present in $dataArray, its value from $list is used
    $result[$key] = $value;
}

If you move the if (! is_array($value)) block outside the if (array_key_exists()) block it will convert to arrays also the values of $list that are not arrays and are associated with keys that are not present in $dataArray (e.g $list['objects']). This way, after the code runs, all values of $result are arrays.


Your code is good as well. Apart from iterating over $list and not over $dataArray, there is no way to make it faster or easier to read in a spectacular manner. The code I suggest here is just another way to write the same thing.

Here is two lines solution ( but you can simply make it one line:) )

$list = [
    'fruit' => [],
    'animals' => [],
    'objects' => [],
];

$dataArray = [
    'fruit' => 'apple',
    'animals' => ['dog', 'cat'],
    'asd' => 'bla'
];   

$list = array_merge($list, array_intersect_key($dataArray, $list));
array_walk($list, function(&$item){$item = (array)$item;});

My approach will do 3 very simple operations:

  1. Run a single loop while modifying $list by reference using &$v.
  2. Ignore iterations where a matching key is not found in $dataArray.
  3. Hard-cast each qualifying value from $dataArray as an array and overwrite the initial empty subarray. If the qualifying value is not an array, it will become the lone element in the generated subarray; if it is, then nothing about the subarray is changed.

Code: (Demo)

$list = [
    'fruit' => [],
    'animals' => [],
    'objects' => []
];

$dataArray = [
    'fruit' => 'apple',
    'animals' => ['dog', 'cat'],
    'asd' => 'bla'
];

foreach ($list as $k => &$v) {
    if (isset($dataArray[$k])) {
        $v = (array)$dataArray[$k];
    }
}

var_export($list);

Output:

array (
  'fruit' => 
  array (
    0 => 'apple',
  ),
  'animals' => 
  array (
    0 => 'dog',
    1 => 'cat',
  ),
  'objects' => 
  array (
  ),
)
Related