Guilherme do Valle
Guilherme do Valle

Reputation: 508

How can i merge items of a Laravel Collection based on keys?

I have a Laravel Collection with a lot of duplicated items like that:

[
  id: 'NAME1',
  prop1: 'yes',
  prop2: null,
  prop3: 'bla',
  prop4: null
],
[
  id: 'NAME1',
  prop1: null,
  prop2: 'yes'
  prop3: null,
  prop4: 'bla'
]

And i want to merge the elements with the same 'id' property, and get a collection like that, preserving both properties:

[
  id: 'NAME1',
  prop1: 'yes',
  prop2: 'yes',
  prop3: 'bla',
  prop4: 'bla'
]

When i use $collection->unique('id') i only get a collection like that, losing the prop2 and prop4 of the second element:

[
  id: 'NAME1',
  prop1: 'yes',
  prop2: null,
  prop3: 'bla',
  prop4: null
]

How can i solve it? I doesn't find any method of Laravel Collections which could merge elements of a Collection when one of the elements had a null key.

Upvotes: 1

Views: 1869

Answers (2)

kburlz
kburlz

Reputation: 606

Here's a macro that will do what you want:

use Illuminate\Support\Collection;

Collection::macro('mergeByKey', function ($key) {
    return $this->groupBy($key)->map(function($group) {

        $filteredGroup = collect($group)->map(function($item) {
            return collect($item)->reject(function($value, $key) {
                return $value === null; 
            });
        });

        return array_merge(...$filteredGroup->toArray());
    })->values();
});

Then you can use it on a collection like this:

$collection = collect([
    [
        'id' => 'NAME1',
        'prop1' => 'yes',
        'prop2' => null,
        'prop3' => 'bla',
        'prop4' => null
    ],
    [
        'id' => 'NAME1',
        'prop1' => null,
        'prop2' => 'yes',
        'prop3' => null,
        'prop4' => 'bla'
    ],
    [
        'id' => 'NAME2',
        'prop1' => null,
        'prop2' => 'fdsa',
        'prop3' => null,
        'prop4' => 'asdf'
    ],
    [
        'id' => 'NAME2',
        'prop1' => 'fdsa',
        'prop2' => null,
        'prop3' => 'asdf',
        'prop4' => null
    ],
]);


$result = $collection->mergeByKey('id');

Result:

Collection {#268 ▼
  #items: array:2 [▼
    0 => array:5 [▼
      "id" => "NAME1"
      "prop1" => "yes"
      "prop3" => "bla"
      "prop2" => "yes"
      "prop4" => "bla"
    ]
    1 => array:5 [▼
      "id" => "NAME2"
      "prop2" => "fdsa"
      "prop4" => "asdf"
      "prop1" => "fdsa"
      "prop3" => "asdf"
    ]
  ]
}

Upvotes: 1

Weisskopf
Weisskopf

Reputation: 101

So you want to merge all non-null properties for each id (you have only one ID in your list, but I assume there can be many) 1) group by id and get the list of [id => [all property lists for id]] 2) for each id: 2a) remove empty properties from each list 2b) merge all lists into one

It can be done this way with laravel collections:

$data = '[
{
"id": "NAME1",
"prop1": "yes",
"prop2": null,
"prop3": "bla",
"prop4": null
},
{
"id": "NAME1",
"prop1": null,
"prop2": "yes",
"prop3": null,
"prop4": "bla"
},
{
"id": "NAME2",
"prop1": "no",
"prop2": "dah",
"prop4": "bla"
}

]
';

      $coll = collect(json_decode($data, JSON_OBJECT_AS_ARRAY))
        ->groupBy('id')
        ->map(function ($propGroup) {
          //for each group of 'objects' of property lists
          return $propGroup
            ->map(function ($props) {
              //remove empty properties
              return collect($props)->filter(function ($prop) {
                return !empty($prop);
              });
            })
            ->reduce(function ($carry, $item) {
              return $carry->merge($item);
            }, collect());
        });

Upvotes: 0

Related Questions