Ryan Mortier
Ryan Mortier

Reputation: 883

Merge Two Laravel Collections and Sum the Values

I have 10 (3 used for the example below) collections I need to merge together by key but need to sum the values. My collections look like this:

[
    'key1' => ['value1' => 5, 'value2' => 3, 'value3' => 0],
    'key2' => ['value1' => 1, 'value2' => 6, 'value3' => 2],
    'key3' => ['value1' => 0, 'value2' => 0, 'value3' => 1],
]

[
    'key1' => ['value1' => 3, 'value2' => 1, 'value3' => 7],
    'key2' => ['value1' => 1, 'value2' => 3, 'value3' => 2],
    'key3' => ['value1' => 1, 'value2' => 6, 'value3' => 1],
]

[
    'key1' => ['value1' => 2, 'value2' => 3, 'value3' => 9],
    'key2' => ['value1' => 3, 'value2' => 8, 'value3' => 3],
    'key3' => ['value1' => 1, 'value2' => 0, 'value3' => 6],
]

I'd like to get one collection where the result looks like this:

[
    'key1' => ['value1' => 10, 'value2' => 7, 'value3' => 16],
    'key2' => ['value1' => 5, 'value2' => 17, 'value3' => 7],
    'key3' => ['value1' => 2, 'value2' => 6, 'value3' => 8],
]

Is there a built in Laravel Collection method I'm missing or what is the best way to achieve this?

edit: (more info) The values are always numeric. There are no string values in any of the collections.

edit2: Some people are asking what I've tried. I've tried this which works:

$merged = [];
foreach ($seeds as $seed) {
    foreach ($seed as $location => $values) {
        foreach ($values as $key => $value) {
            $merged[$location][$key] = $value + ($merged[$location][$key] ?? 0);
        }
    }
}

I was just wondering if there was a more succinct way of achieving the same thing instead of using 3 foreach loops.

Upvotes: 4

Views: 5383

Answers (2)

Enric Mir Ramos
Enric Mir Ramos

Reputation: 11

I did this with laravel 7 in case it works for someone

$test = collect([
   [
     'key1' => ['value1' => 5, 'value2' => 3, 'value3' => 0],
     'key2' => ['value1' => 1, 'value2' => 6, 'value3' => 2],
     'key3' => ['value1' => 0, 'value2' => 0, 'value3' => 1],
   ],

   [
     'key1' => ['value1' => 3, 'value2' => 1, 'value3' => 7],
     'key2' => ['value1' => 1, 'value2' => 3, 'value3' => 2],
     'key3' => ['value1' => 1, 'value2' => 6, 'value3' => 1],
   ],

   [
     'key1' => ['value1' => 2, 'value2' => 3, 'value3' => 9],
     'key2' => ['value1' => 3, 'value2' => 8, 'value3' => 3],
     'key3' => ['value1' => 1, 'value2' => 0, 'value3' => 6],
   ]
]);
$col = $test->reduce(function ($carry, $item) {
    return $carry->mergeRecursive($item);
}, collect([]));
$col->transform(function($item) {
    return collect($item)->map(function ($item2) {
        return collect($item2)->sum();
    });
});

Upvotes: 1

Sérgio Reis
Sérgio Reis

Reputation: 2523

Really thought there would be something easier to do than loop but it seems that solution is still better than the one i came up with. Flatten removes the key even if you change the depth, mapToGroups only works while returning an associative array containing a single key / value pair, pluck brings some other difficulties and so on.

Here's what i came up with

$test = collect([
   [
     'key1' => ['value1' => 5, 'value2' => 3, 'value3' => 0],
     'key2' => ['value1' => 1, 'value2' => 6, 'value3' => 2],
     'key3' => ['value1' => 0, 'value2' => 0, 'value3' => 1],
   ],

   [
     'key1' => ['value1' => 3, 'value2' => 1, 'value3' => 7],
     'key2' => ['value1' => 1, 'value2' => 3, 'value3' => 2],
     'key3' => ['value1' => 1, 'value2' => 6, 'value3' => 1],
   ],

   [
     'key1' => ['value1' => 2, 'value2' => 3, 'value3' => 9],
     'key2' => ['value1' => 3, 'value2' => 8, 'value3' => 3],
     'key3' => ['value1' => 1, 'value2' => 0, 'value3' => 6],
   ]
]);
$mappedCollection = collect($test->first())->keys()->mapWithKeys(function($item,$key) use($test){
   return[
      $item => $test->map(function ($mapItem, $mapKey) use($item) {         
         return $mapItem[$item];
      })
   ];
})->mapWithKeys(function($item,$key){
   $eachLine = collect($item->first())->keys()->mapWithKeys(function($mapItem) use($item){
      return[ $mapItem => $item->sum($mapItem)  ];
   });       
   return [$key =>  $eachLine];
})->all();

dd($mappedCollection);

Source for collections documentation https://laravel.com/docs/5.6/collections

Upvotes: 0

Related Questions