9

Let's say I have following arrays:

$first = [
    ['id' => 5, 'name' => 'Education'],
    ['id' => 4, 'name' => 'Computers'],
    ['id' => 7, 'name' => 'Science'],
    ['id' => 1, 'name' => 'Sports'],
];

$second = [
    ['id' => 1, 'title' => 'Sport'],
    ['id' => 7, 'title' => 'Sci'],
    ['id' => 4, 'title' => 'Comp'],
    ['id' => 5, 'title' => 'Edu'],
];

And desired output is:

[
    ['id' => 5, 'name' => 'Education', 'title' => 'Edu'],
    ['id' => 4, 'name' => 'Computers', 'title' => 'Comp'],
    ['id' => 7, 'name' => 'Science', 'title' => 'Sci'],
    ['id' => 1, 'name' => 'Sports', 'title' => 'Sport'],
]

I have managed to merge these arrays with simply:

foreach ($first as $key => $value) {
    $result[$key] = array_merge($first[$key], $second[$key]);
}

But the output is combined by first level index instead of their id value:

Array
    (
        [0] => Array
            (
                [id] => 5
                [name] => Education
                [title] => Sport
            )

        [1] => Array
            (
                [id] => 4
                [name] => Computers
                [title] => Sci
            )

        [3] => Array
            (
                [id] => 7
                [name] => Science
                [title] => Comp

        [4] => Array
            (
                [id] => 1
                [name] => Sports
                [title] => Edu
            )
    )

I would like to append the title values from the second array to the first array where there is a matching id value and maintain the sorting of the first array.

How can I achieve this?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
user1292810
  • 1,702
  • 7
  • 19
  • 38
  • Related page where rows are indexed instead of associative and the two arrays do not have the same length/ids: [Merge two indexed arrays of indexed arrays based on first column value](https://stackoverflow.com/q/8879767/2943403) – mickmackusa Aug 21 '23 at 22:16

7 Answers7

11

You can just do a nested loop and check if the id values match, then add title to $first (or name to $second)

foreach($first as $key => $value){
    foreach($second as $value2){
        if($value['id'] === $value2['id']){
            $first[$key]['title'] = $value2['title'];
        }               
    }
}
WebChemist
  • 4,393
  • 6
  • 28
  • 37
2

You code works fine. Your expectations are simply incorrect. For example in one array 4th element id holds 1 but in another array, 4th element id is 5, so your "merge these arrays on the same id" makes no sense as by merging 4th elements into one you also merge their children, and since id is used in both arrays, once value HAVE TO be gone as there cannot be two equal keys in array.

EDIT

you have to merge manually as PHP functions merge based on keys while you want to merge based on content:

$result = array();
foreach( $arrayA as $keyA => $valA ) {
  foreach( $arrayB as $keyB => $valB ) {
     if( $valA['id'] == $valB['id'] ) {
       $result[$keyA] = $valA + $valB;

       // or if you do not care output keys, just
       // $result[] = $valA + $valB;
     }
  }
}
Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
  • I have corrected some mistakes in outputs, sorry for that. What I want is to take e.g `$first[0]['id']` (equals `5`) find same `id` in the second array, here `$second[4]['id']` and merge them into new array like so: `array('id'=>5, 'name'=>'Education', 'title'=>'Edu')`. Isn't that possible? – user1292810 Dec 02 '12 at 13:30
  • Works like a charm, thanks :) And sorry for misleading title. – user1292810 Dec 02 '12 at 13:47
  • No early `break`ing when a match is found? – mickmackusa Sep 30 '21 at 03:54
2

To provide an alternative approach available in PHP 5.5+.

Since the ordering of the two arrays is not identical, first use array_column to index the arrays by the id.

This will allow you to use array_replace_recusrive on the id indexed arrays.

array_replace_recursive will merge the values in the arrays matching the index association of both array sets.

Optionally use array_values to reindex the array, removing the id index association.

Example https://3v4l.org/ndv2j

$first = array_column($first, null, 'id');
$second = array_column($second, null, 'id');
$result = array_values(array_replace_recursive($first, $second));

Result

Array
(
    [0] => Array
        (
            [id] => 5
            [name] => Education
            [title] => Edu
        )

    [1] => Array
        (
            [id] => 4
            [name] => Computers
            [title] => Comp
        )

    [2] => Array
        (
            [id] => 7
            [name] => Science
            [title] => Sci
        )

    [3] => Array
        (
            [id] => 1
            [name] => Sports
            [title] => Sport
        )

)
Will B.
  • 17,883
  • 4
  • 67
  • 69
1

Do not use foreach in foreach,that might be too slow when the array so big.

$idArray = array_column($secondArray,'title','id');
foreach($firstArray as $key => $val){
  $firstArray[$key]['title'] = (isset($idArray[$val['id']])) ? $idArray[$val['id']] : 'some title';
}
Jasim Juwel
  • 736
  • 8
  • 19
1

This can be easily done using the 3-parameter form of array_column to re-index your second array by the id value and then looping over the first array, merging the contents of the matching second value (where it exists):

$second = array_column($second, null, 'id');
foreach ($first as &$subject) {
    $subject = array_merge($subject, $second[$subject['id']] ?? []);
}

Output:

Array
(
    [0] => Array
        (
            [id] => 5
            [name] => Education
            [title] => Edu
        )
    [1] => Array
        (
            [id] => 4
            [name] => Computers
            [title] => Comp
        )
    [3] => Array
        (
            [id] => 7
            [name] => Science
            [title] => Sci
        )
    [4] => Array
        (
            [id] => 1
            [name] => Sports
            [title] => Sport
        )
)

Demo on 3v4l.org

This has the advantage over Will B.'s answer of not re-indexing the first array. It's also slightly faster (my tests show ~10%) due to only having one call to array_column and none to array_values.

Update

This code can actually be sped up even more by using the array union operator (+) (thanks @mickmackusa):

$second = array_column($second, null, 'id');
foreach ($first as &$subject) {
    $subject += $second[$subject['id']] ?? [];
}

The output of this code is the same as above, but it runs about 10% faster again.

Demo on 3v4l.org

Nick
  • 138,499
  • 22
  • 57
  • 95
  • This answer is probably as optimized for speed as it can be, but bear in mind that the call of `array_column()` will lose data IF there are duplicate `id` column values in the `$second` array. (This is just a fringe consideration for readers, because the sample data in the question does not have duplicated `id` column values in the `$second` array.) – mickmackusa Aug 21 '23 at 21:59
  • Another fringe scenario would be if the `$first` array did not contain an `id` value that corresponds to the `$second` array, the data from the `$second` would be omitted from the result. This may or may not be a desired behavior. In the posted question all available `id` values are represented in both arrays. – mickmackusa Aug 21 '23 at 22:14
0

Sort both arrays by your 'id' field, then let php do the merge with array_replace_recursive.

function cmp($a, $b) {
  return ((int) $a['id'] < (int) $b['id']) ? -1 : 1;
}

usort($array1, 'cmp');
usort($array2, 'cmp');

$result = array_replace_recursive($array1, $array2);
jmlnik
  • 2,867
  • 2
  • 17
  • 20
  • This untested snippet is clearly incorrect in its output. – mickmackusa Oct 12 '20 at 01:04
  • Hey @mickmackusa, well spotted. This code works fine with `array_replace_recursive` but not with `array_merge`, obviously! – jmlnik Sep 30 '21 at 02:53
  • I still don't recommend this technique because `usort()` will require greater computation complexity versus other answers on this page. I'm biased because I cooperated with Nick already, but -- I'd use Nick's answer. This assumes that both arrays contain the same volume and data structure. It also requires both arrays to share the same ids. This answer is not reliable for general use. – mickmackusa Sep 30 '21 at 03:56
0

Make sure that the items are in the same order then:

$items = array_map(function($itemFirstArray, $itemSecondArray) {
  return array_merge($itemFirstArray, $itemSecondArray);
}, $firstArray, $secondArray);
Pol Dellaiera
  • 127
  • 1
  • 8
  • It probably makes sense to not yatta-yatta over the bit of code that ensures that the two arrays are sorted correctly. To be clear, this technique requires that both arrays contain exactly and only the same id-related data. – mickmackusa Oct 12 '20 at 01:28
  • This answer requires that both arrays are the same length and have exactly the same mapping column values. – mickmackusa Aug 21 '23 at 22:02