基于键的值合并数组

7

我有两个数组,每个数组都包含一个id键,我想根据该数组的键和键值将数据合并在一起。数据可能如下所示:

    $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'],
    ];

有没有特别高效的函数或技巧可以处理这样的情况?还是我应该只遍历一个数组的元素并将内容推到另一个数组中?

我还在使用Laravel,数据是由一个Eloquent查询结果生成的,所以如果它可以使代码更清晰,我也可以利用集合。

9个回答

5

使用array_replace_recursive函数,可以轻松快捷地进行数组替换操作。

array_replace_recursive($color, $size)

1
Maninderpreet Singh是正确的,只要确保你理解这个方法的工作原理,以及如果数组除了“id”之外还有任何重叠的键会发生什么。 - Alex Harris
1
array_replace_recursive() 函数在索引(未准备好)的子数组上的使用之所以有效,是因为提供的数据具有关联的子数组,这些子数组也共享第一级索引。请参阅我的答案 了解为什么这不是一个稳定的解决方案以及如何使其稳定。 - mickmackusa

4
纯PHP解决方案是使用array_replace_recursive,示例代码如下:
array_replace_recursive(
  array_combine(array_column($color, "id"), $color),
  array_combine(array_column($size, "id"), $size)
);

你应该注意到array_replace_recursive通过键合并数组。 因此,如果你从数据库获取这样的数据:
$color = [
    ['id' => 1, 'color' => 'red'],
    ['id' => 2, 'color' => 'red']
];

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

array_replace_recursive函数会返回损坏的合并结果:

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

解决方案是将array_replace_recursivearray_columnarray_combine相结合,通过它们的id字段合并数组:
array_replace_recursive(
  array_combine(array_column($color, "id"), $color),
  array_combine(array_column($size, "id"), $size)
);

array_combine(array_column($color, "id"), $color) 创建以 id 作为键的关联数组。

因此,在您的情况下,它将返回:

$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() 不是准备数组所必需的。请参见我的答案 - mickmackusa

1
您可以使用array_replace_recursive函数来合并您特定情况下的数组。
$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);

输出:

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"
  }
}

注意:我使用传统的数组布局,因为我的PHP版本还不支持新的布局 :)

第二个选项

您也可以使用array_map。 这将让您进行一些微调,从而添加尽可能多的数组。

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

var_dump($merged); // See output above

1
array_replace_recursive() 函数在索引(未准备好)的子数组上的使用之所以有效,是因为提供的数据具有关联的子数组,这些子数组也共享第一级索引。请参阅我的答案 了解为什么这不是一个稳定的解决方案以及如何使其稳定。 - mickmackusa

0

简单的嵌套循环可以解决问题。

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

输出:

   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
    )

)

嵌套循环的效率与准备好的array_replace_recursive()解决方案可能需要使用实际项目数据进行基准测试,而且差异可能无论如何都难以区分。调用array_column()两次以建立关联键,然后使用array_replace_recursive()将对每个输入数组进行1次完整遍历,然后执行“合并”替换。嵌套循环(如果它们包括条件性break)不需要进行函数调用或对第二个数组进行完整遍历,但它会重新访问数据。请参见我的答案 - mickmackusa
1
此外,使用嵌套循环方法将决定第一个数组控制哪些第二个数组子数组允许出现在输出数组中。如果第二个数组具有第一个数组中未表示的“id”,则数据将丢失。这只是需要注意的一点。 - mickmackusa

0
我建议使用Laravel的集合,因为这个问题有Laravel标签。
$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);

0
您需要逐个遍历两个数组中的所有元素,并合并任何重复的元素。为此,您需要执行以下步骤。
  1. 将两个数组粘合在一起。

     $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. 循环遍历组合后的数组,将项目复制到一个新数组中。

  3. 如果ID之前已经出现过,则不要追加该行,而是将其与现有行合并。

你可以通过一个单独的 foreach 循环和 array_merge 来实现所有这些。不需要递归函数或嵌套循环。
// 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;
}

唯一的缺点是您会失去原始索引,但从您的问题来看,这对您并不重要。如果您想重新索引数组,请使用以下方法:
$merged = array_values($merged);

实时演示


0

请注意: array_replace_recursive() 是递归的:它将递归进入数组并对内部值应用相同的过程。

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

然后你可以打印出来看结果如下:

print_r($combined);

仅在索引(未准备)的子数组上使用 array_replace_recursive() 之所以有效,是因为提供的数据具有关联的子数组,这些子数组还共享第一级索引。请参见我的答案,了解为什么这不是一个稳定的解决方案,以及如何使其稳定。 - mickmackusa

-1

尝试:

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

输出:

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
        )

)

-2
更好的方法是使用循环。首先计算最大数组,然后通过计数此数字运行循环。它会起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接