7

I have two arrays of arrays that have an id key, and I'd like to merge the data together based on that array's key and key value. The data would look something like:

    $color = [
        ['id' => 1, 'color' => 'red'],
        ['id' => 2, 'color' => 'green'],
        ['id' => 3, 'color' => 'blue'],
    ];

    $size = [
        ['id' => 1, 'size' => 'SM'],
        ['id' => 2, 'size' => 'XL'],
        ['id' => 3, 'size' => 'MD'],
        ['id' => 4, 'size' => 'LG'],
    ];

    $combined = [
        ['id' => 1, 'color' => 'red', 'size' => 'SM'],
        ['id' => 2, 'color' => 'green', 'size' => 'XL'],
        ['id' => 3, 'color' => 'blue', 'size' => 'MD'],
        ['id' => 4, 'size' => 'LG'],
    ];

Is there a particularly efficient function or trick for handling something like this? Or should I just loop through the elements of one array and push the contents to the other?

I'm also using Laravel, and the data is a result of an eloquent query, so I can also utilize the collections if it would make the code cleaner.

kenshin9
  • 2,215
  • 4
  • 23
  • 38

9 Answers9

5

Use array_replace_recursive function for easy and fast way

array_replace_recursive($color, $size)
Maninderpreet Singh
  • 2,569
  • 2
  • 17
  • 31
  • 1
    Maninderpreet Singh is correct, just make sure to you understand how the method works and what would happen if the arrays had any overlapping keys besides `id`. – Alex Harris Jul 15 '16 at 12:58
  • 1
    The use of `array_replace_recursive()` on the indexed (unprepared) subarrays, only works because the supplied data has associated subarrays which also share first level indexes. See [my answer](https://stackoverflow.com/a/59702181/2943403) to understand why this is not a stable solution and how to stabilize it. – mickmackusa Jan 12 '20 at 21:00
4

Pure php solution is to use array_replace_recursive like this:

array_replace_recursive(
  array_combine(array_column($color, "id"), $color),
  array_combine(array_column($size, "id"), $size)
);

You should notice that array_replace_recursive merge arrays by keys. So, if you get such data from database:

$color = [
    ['id' => 1, 'color' => 'red'],
    ['id' => 2, 'color' => 'red']
];

$size = [
    ['id' => 2, 'size' => 'SM']
];

array_replace_recursive will return corrupted merge:

$combined = [
    ['id' => 2, 'color' => 'red', 'size' => 'SM'],
    ['id' => 2, 'color' => 'red']
];

The solution is to combine array_replace_recursive with array_column and array_combine for merging arrays by their's id field:

array_replace_recursive(
  array_combine(array_column($color, "id"), $color),
  array_combine(array_column($size, "id"), $size)
);

array_combine(array_column($color, "id"), $color) creates associative array with id as keys.

So, in your case it will return:

$combined = [
    1 => ['id' => 1, 'color' => 'red', 'size' => 'SM'],
    2 => ['id' => 2, 'color' => 'green', 'size' => 'XL'],
    3 => ['id' => 3, 'color' => 'blue', 'size' => 'MD'],
    4 => ['id' => 4, 'size' => 'LG'],
];
  • 1
    `array_combine()` is not necessary to prepare the arrays. See [my answer](https://stackoverflow.com/a/59702181/2943403). – mickmackusa Jan 12 '20 at 21:13
1

You can use array_replace_recursive to merge the arrays in your particular situation.

$color = array(
    array('id' => 1, 'color' => 'red'),
    array('id' => 2, 'color' => 'green'),
    array('id' => 3, 'color' => 'blue'),
);

$size = array(
    array('id' => 1, 'size' => 'SM'),
    array('id' => 2, 'size' => 'XL'),
    array('id' => 3, 'size' => 'MD'),
    array('id' => 4, 'size' => 'LG'),
);

$merged = array_replace_recursive($color, $size);

Output:

array(4) {
  [0]=>
  array(3) {
    ["id"]=>
    int(1)
    ["color"]=>
    string(3) "red"
    ["size"]=>
    string(2) "SM"
  }
  [1]=>
  array(3) {
    ["id"]=>
    int(2)
    ["color"]=>
    string(5) "green"
    ["size"]=>
    string(2) "XL"
  }
  [2]=>
  array(3) {
    ["id"]=>
    int(3)
    ["color"]=>
    string(4) "blue"
    ["size"]=>
    string(2) "MD"
  }
  [3]=>
  array(2) {
    ["id"]=>
    int(4)
    ["size"]=>
    string(2) "LG"
  }
}

Note: I used the traditional array layout because my PHP version won't support the new one yet :)

Second option

You can also use array_map. This will let you add as much arrays as you want with a little tweaking.

$merged = array_map(function ($c, $s) {
    return array_merge($c, $s);
}, $color, $size);

var_dump($merged); // See output above
Peter
  • 8,776
  • 6
  • 62
  • 95
  • 1
    The use of `array_replace_recursive()` on the indexed (unprepared) subarrays, only works because the supplied data has associated subarrays which also share first level indexes. See [my answer](https://stackoverflow.com/a/59702181/2943403) to understand why this is not a stable solution and how to stabilize it. – mickmackusa Jan 12 '20 at 21:00
0

Simple nested loop would solve the purpose.

foreach($size as $key => $value1) {
    foreach($color as $value2) {
        if($value1['id'] === $value2['id']){
            $size[$key]['color'] = $value2['color'];
        }               
    }
}
echo '<pre>';
print_r($size);

Output:

   Array
  (
   [0] => Array
    (
        [id] => 1
        [size] => SM
        [title] => red
    )

  [1] => Array
    (
        [id] => 2
        [size] => XL
        [title] => green
    )

[2] => Array
    (
        [id] => 3
        [size] => MD
        [title] => blue
    )

[3] => Array
    (
        [id] => 4
        [size] => LG
    )

)
Dharman
  • 30,962
  • 25
  • 85
  • 135
Indrasis Datta
  • 8,692
  • 2
  • 14
  • 32
  • The efficiency of nested loops versus a prepared `array_replace_recursive()` solution may require benchmarking with actual project data -- and the difference may well be indistinguishable by human's anyhow. Calling `array_column()` twice to establish associative keys then `array_replace_recursive()` will make 1 full pass over each input array then perform the _merging_ replacement. Nested loops (if they include a conditional `break`) won't need to make function calls or full passes over the second array, but it will revisit data. See [my answer](https://stackoverflow.com/a/59702181/2943403). – mickmackusa Jan 12 '20 at 21:11
  • 1
    Additionally, using a nested loop approach will dictate that the first array controls which second array subarrays are permitted in the output array. If the second array has an `id` not represented in the first array, then data will be lost. Just something to be aware of. – mickmackusa Jan 12 '20 at 21:17
0

I'd suggest using laravel's collections, since this question has the laravel tag.

$color = collect(
    ['id' => 1, 'color' => 'red'],
    ['id' => 2, 'color' => 'green'],
    ['id' => 3, 'color' => 'blue']
);

$size = collect(
    ['id' => 1, 'size' => 'SM'],
    ['id' => 2, 'size' => 'XL'],
    ['id' => 3, 'size' => 'MD'],
    ['id' => 4, 'size' => 'LG']
);

$combined = $color->merge($size);
Ivanka Todorova
  • 9,964
  • 16
  • 66
  • 103
0

Folllow this: array_replace_recursive() is recursive : it will recurse into arrays and apply the same process to the inner value.

$combined = array_replace_recursive($color, $size);

then you can print to see the result as bellow:

print_r($combined);
Mahdi
  • 206
  • 2
  • 14
  • The use of `array_replace_recursive()` on the indexed (unprepared) subarrays, only works because the supplied data has associated subarrays which also share first level indexes. See [my answer](https://stackoverflow.com/a/59702181/2943403) to understand why this is not a stable solution and how to stabilize it. – mickmackusa Jan 12 '20 at 21:01
0

You need to traverse all elements from both arrays one by one and merge any duplicate elements. To do this you need to perform these steps.

  1. Glue both arrays together.

     $arr = [
         ['id' => 1, 'color' => 'red'],
         ['id' => 2, 'color' => 'green'],
         ['id' => 3, 'color' => 'blue'],
         ['id' => 1, 'size' => 'SM'],
         ['id' => 2, 'size' => 'XL'],
         ['id' => 4, 'size' => 'LG'],
         ['id' => 3, 'size' => 'MD'],
     ];
    
  2. Loop the combined array copying the items into a new array.

  3. If the ID has been seen before don't append the row, but merge it with an existing one.

You can achieve all of this with a single foreach loop and array_merge. There is no need for recursive functions or nested loops.

// Loops on merged arrays and copied elements into a new array
foreach(array_merge($color, $size) as $el){
    // If the element with this id is already in new array then add the elements together
    $merged[$el['id']] = ($merged[$el['id']] ?? []) + $el;
}

The only downside is that you lose the original indexes, but it looks from your question that this was not important to you. If you want, you can reindex the array with

$merged = array_values($merged);

Live Demo

Dharman
  • 30,962
  • 25
  • 85
  • 135
-1

Try:

$out = array();
foreach ($size as $key => $value){
    if(!isset($color[$key])) { $color[$key] = array(); }  
    $out[] = array_merge((array)$value,(array)$color[$key]);
}

Output:

Array
(
    [0] => Array
        (
            [id] => 1
            [size] => SM
            [color] => red
        )

    [1] => Array
        (
            [id] => 2
            [size] => XL
            [color] => green
        )

    [2] => Array
        (
            [id] => 3
            [size] => MD
            [color] => blue
        )

    [3] => Array
        (
            [id] => 4
            [size] => LG
        )

)
Dhara Parmar
  • 8,021
  • 1
  • 16
  • 27
-2

Better way using loop. First calculate the max array the by count this number run a loop. It will work.

Banty Roy
  • 914
  • 6
  • 23