Merge two multidimensional arrays and remove duplicate values within indexed subarrays
Asked Answered
H

2

15

I need to merge those two arrays:

$ar1 = array("color" => array("red", "green"), "aa");
$ar2 = array("color" => array( "green", "blue"), "bb");
$result = array_merge_recursive($ar1, $ar2);

Expected output:

[
    'color' => [
        (int) 0 => 'red',
        (int) 1 => 'green',
        (int) 3 => 'blue'
    ],
    (int) 0 => 'aa',
    (int) 1 => 'bb'
]

But it outputs:

[
    'color' => [
        (int) 0 => 'red',
        (int) 1 => 'green',
        (int) 2 => 'green', (!)
        (int) 3 => 'blue'
    ],
    (int) 0 => 'aa',
    (int) 1 => 'bb'
]

I'm looking for the simplest way to do this, my array inputs won't be deeper than those examples.

Heroin answered 7/9, 2014 at 15:53 Comment(4)
First comment in php.net/manual/en/function.array-merge-recursive.phpBareback
Already tested it, but it doesn't work. it outputs: ['color' => [ 'geen', 'blue'], 'bb']Heroin
The manual explains, see 'Description', that it will only merge 'string' keys, 'numeric' keys will be appended. Sadly, this prevents the 'array_merge_recursive' function doing what you require without modification of the 'keys'. May i suggest that your code would be more reliable with 'named' keys anyway?Yorke
I cant, its part of huge conf so i have to group datas.Heroin
B
39

Here it is.

function array_merge_recursive_ex(array $array1, array $array2)
{
    $merged = $array1;

    foreach ($array2 as $key => & $value) {
        if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
            $merged[$key] = array_merge_recursive_ex($merged[$key], $value);
        } else if (is_numeric($key)) {
             if (!in_array($value, $merged)) {
                $merged[] = $value;
             }
        } else {
            $merged[$key] = $value;
        }
    }

    return $merged;
}
Bareback answered 7/9, 2014 at 16:29 Comment(4)
Another good example, similar to this - from drupal: api.drupal.org/api/drupal/includes!bootstrap.inc/function/…Amberambergris
Actually, you don't need to pass by reference here anything.Chittagong
I would move the if (is_numeric($key)) to be first, so that numeric arrays will get merged instead of overwrittenTalkative
If you would like to preserve numeric keys I would suggest you change $merged[] = $value; to $merged[$key] = $value;. Without this change numeric key values are ignored.Hyoscyamine
A
6

Thanks to Meglio comment, you can use these functions for any number of arrays :

Functions

function drupal_array_merge_deep() {
  $args = func_get_args();
  return drupal_array_merge_deep_array($args);
}

// source : https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_array_merge_deep_array/7.x
function drupal_array_merge_deep_array($arrays) {
    $result = array();
    foreach ($arrays as $array) {
        foreach ($array as $key => $value) {
            // Renumber integer keys as array_merge_recursive() does. Note that PHP
            // automatically converts array keys that are integer strings (e.g., '1')
            // to integers.
            if (is_integer($key)) {
                $result[] = $value;
            }
            elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
                $result[$key] = drupal_array_merge_deep_array(array(
                    $result[$key],
                    $value,
                ));
            }
            else {
                $result[$key] = $value;
            }
        }
    }
    return $result;
}

Usage

$merged = drupal_array_merge_deep($ar_1, $ar_2);

var_dump($merged);

$merged = drupal_array_merge_deep_array([$ar_1, $ar_2]);

var_dump($merged);

Usage (test data)

$ar_1 = [
    "item1" => false,
    "item2" => true,
    "item_list" => [
        "sub_item1" => 5,
        "sub_itemlist" => [
            "sub_sub_item1" => 27,
        ],
    ]
];

$ar_2 = [
    "item_list" => [
        "sub_item2" => 5,
        "sub_itemlist" => [
            "sub_sub_item2" => 27,
        ],
    ],
    "item3" => true,
];

Usage output (same for both functions)

array (size=4)
  'item1' => boolean false
  'item2' => boolean true
  'item_list' => 
    array (size=3)
      'sub_item1' => int 5
      'sub_itemlist' => 
        array (size=2)
          'sub_sub_item1' => int 27
          'sub_sub_item2' => int 27
      'sub_item2' => int 5
  'item3' => boolean true
Airdrop answered 7/5, 2019 at 14:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.