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

4

6

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.

Lovieloving answered 21/9, 2017 at 10:14 Comment(2)
PHP provides array functions that implement only simple rules. Your rules are not that simple, there is no array function provided by PHP that can merge your arrays. You have to write it yourself (as apparently you already did).Wobbling
With array_walk() you can however use your loop wrapped in a function and it will do all the stuff but there exists no build in function that does all what you want.Chukker
W
2

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.

Wobbling answered 21/9, 2017 at 10:35 Comment(1)
"Spaghetti code" means something else but you are free to choose whatever answer solves your problem. Or you can provide your own answer.Wobbling
V
7

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.

Verbatim answered 21/9, 2017 at 10:25 Comment(2)
there's the problem that 'fruit' now contains only a string, while I wanted to keep it as arrayLovieloving
Ah sorry, I completely missed that. I'm not sure you'll find a way to achieve that with in-built functions. I've added a quick possible solution to the end, which might help.Verbatim
W
2

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.

Wobbling answered 21/9, 2017 at 10:35 Comment(1)
"Spaghetti code" means something else but you are free to choose whatever answer solves your problem. Or you can provide your own answer.Wobbling
C
1

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;});
Cleat answered 29/10, 2019 at 13:12 Comment(0)
D
0

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 (
  ),
)
Drivel answered 10/7, 2018 at 4:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.