【问题标题】:Merging arrays based on a value of the key根据键的值合并数组
【发布时间】:2016-11-18 16:39:09
【问题描述】:

我有两个具有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 查询的结果,所以我也可以使用集合,如果它可以使代码更清晰。

【问题讨论】:

标签: php arrays laravel collections array-merge


【解决方案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】:

    使用array_replace_recursive函数,方便快捷

    array_replace_recursive($color, $size)
    

    【讨论】:

    • Maninderpreet Singh 是正确的,只要确保您了解该方法的工作原理以及如果数组具有除 id 之外的任何重叠键会发生什么。
    • 在索引(未准备)子数组上使用array_replace_recursive(),仅因为提供的数据具有关联的子数组,这些子数组也共享第一级索引。请参阅my answer 了解为什么这不是一个稳定的解决方案以及如何稳定它。
    【解决方案3】:

    使用循环的更好方法。首先计算最大数组,按计数这个数字运行一个循环。它会起作用的。

    【讨论】:

      【解决方案4】:

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

      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)不需要进行函数调用或完全传递第二个数组,但它会重新访问数据。见my answer
      • 此外,使用嵌套循环方法将指示第一个数组控制输出数组中允许哪些第二个数组子数组。如果第二个数组有一个id 没有出现在第一个数组中,那么数据将会丢失。只是需要注意的事情。
      【解决方案5】:

      您可以使用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
      

      【讨论】:

      • 在索引(未准备)子数组上使用array_replace_recursive(),仅因为提供的数据具有关联的子数组,这些子数组也共享第一级索引。请参阅my answer 了解为什么这不是一个稳定的解决方案以及如何稳定它。
      【解决方案6】:

      我建议使用 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);
      

      【讨论】:

        【解决方案7】:

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

        【讨论】:

        • array_combine() 不是准备阵列所必需的。见my answer
        【解决方案8】:

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

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

        然后您可以打印以查看结果如下:

        print_r($combined);
        

        【讨论】:

        • 在索引(未准备)子数组上使用array_replace_recursive(),仅因为提供的数据具有关联的子数组,这些子数组也共享第一级索引。请参阅my answer 了解为什么这不是一个稳定的解决方案以及如何稳定它。
        【解决方案9】:

        您需要一个一个地遍历两个数组中的所有元素并合并所有重复的元素。为此,您需要执行这些步骤。

        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);
        

        Live Demo

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-09-23
          • 1970-01-01
          • 2015-02-03
          • 2019-06-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多