Search code examples
phplaravellaravel-collection

How to keep nested arrays on Laravel collection merge


I'm going crazy with that.

Given I have 2 arrays like


$array1 = [
    "data_to_merge" => ["2020-03-11 16:00:00", "2020-03-24 14:00:00"],
    "data_to_merge_past_year" => ["2019-03-11 16:00:00"],
];

$array2 = [
    "data_to_merge" => [],
    "data_to_merge_past_year" => ["2018-03-11 14:00:00"],
];

My goal is to have result like

all: [
       "data_to_merge" => [
         "2020-03-11 16:00:00",
         "2020-03-24 14:00:00"
       ],
       "data_to_merge_past_year" => [
         "2018-03-11 14:00:00",
         "2018-03-11 16:00:00",
       ],
     ],

I tried simple with

$result = collect($array1)->merge(collect($array2));

But with that approach it will remove the values from data_to_merge from my first array. Is there any Laravel way to get that result? It can be more than 2 arrays, just to demonstrate.

Here is my full example, it's basically the same


$array1 = [
    "host" => "blablubb",
    "data_to_merge" => [
      "2020-03-11 16:00:00",
      "2020-03-24 14:00:00"
    ],
    "data_to_merge_past_year" => [
      "2019-03-11 16:00:00"
    ],
];

$array2 = [
    "host" => "blablubb",
    "data_to_merge" => [],
    "data_to_merge_past_year" => [
      "2018-03-11 16:00:00"
    ],
];

$array3 = [
    "host" => "blablubb",
    "data_to_merge" => [],
    "data_to_merge_past_year" => [],
];

$array4 = [
    "host" => "blablubb",
    "data_to_merge" => [
      "2020-03-04 14:00:00",
      "2020-03-04 17:00:00"
    ],
    "data_to_merge_past_year" => [],
];



$all = collect([$array1, $array2, $array3, $array4]);

$all
    ->groupBy('host')
    ->map(function ($item) {
        if (count($item) > 1) {
            $result = collect([]);

            foreach ($item as $subItem) {
                $result = $result->merge($subItem);
            }
            return $result;
        }

        return $item->first();
    })
    ->values()
    ->toArray();

Thanks guys!


Solution

  • You can use mergeRecursive for this:

    $array1 = [
        "data_to_merge" => ["2020-03-11 16:00:00", "2020-03-24 14:00:00"],
        "data_to_merge_past_year" => ["2019-03-11 16:00:00"],
    ];
    
    $array2 = [
        "data_to_merge" => [],
        "data_to_merge_past_year" => ["2018-03-11 14:00:00"],
    ];
    
    $result = collect($array1)->mergeRecursive(collect($array2));
    

    Result:

    Illuminate\Support\Collection {#1278 ▼
      #items: array:2 [▼
        "data_to_merge" => array:2 [▼
          0 => "2020-03-11 16:00:00"
          1 => "2020-03-24 14:00:00"
        ]
        "data_to_merge_past_year" => array:2 [▼
          0 => "2019-03-11 16:00:00"
          1 => "2018-03-11 14:00:00"
        ]
      ]
    }
    

    From the docs:

    The mergeRecursive method merges the given array or collection recursively with the original collection. If a string key in the given items matches a string key in the original collection, then the values for these keys are merged together into an array, and this is done recursively:

    $collection = collect(['product_id' => 1, 'price' => 100]);
    
    $merged = $collection->mergeRecursive(['product_id' => 2, 'price' => 200, 'discount' => false]);
    
    $merged->all();
    
    // ['product_id' => [1, 2], 'price' => [100, 200], 'discount' => false]