Reputation: 8078
I have such an array, how can I group by date
and merge each group?
$input = [
[
'date' => '2018-09-25',
'foo' => 'value1',
],
[
'date' => '2018-09-25',
'bar' => 'value2'
],
[
'date' => '2018-09-26',
'baz' => 'value3'
]
];
That in the end it turned out like this:
[
[
'date' => '2018-09-25'
'foo' => 'value1'
'bar' => 'value2'
],
[
'date' => '2018-09-26'
'baz' => 'value3'
]
]
Upvotes: 0
Views: 8572
Reputation: 47874
To group row data from a 2d array in a single loop, use foreach()
or array_reduce()
and a reference array to avoid applying temporary first level keys to the result array.
While less visually elegant than chaining several Laravel collection methods together, there is only one loop used to directly achieve the result. Compare this to call several under-the-hood loops: collect()
, groupBy()
, map()
, array_merge()
, values()
, then toArray()
.
Code: (Demo)
$result = [];
foreach ($input as $row) {
$date = $row['date'];
if (!isset($ref[$date])) {
$ref[$date] = $row;
$result[] =& $ref[$date];
continue;
}
$ref[$date] += $row;
}
var_export($result);
The functional-style equivalent: (Demo)
var_export(
array_reduce(
$input,
function ($result, $row) {
static $ref = [];
$date = $row['date'];
if (!isset($ref[$date])) {
$ref[$date] = $row;
$result[] =& $ref[$date];
} else {
$ref[$date] += $row;
}
return $result;
}
)
);
If you are unmoved by the performance argument, then the following script will concisely chain @ИльяЗелень's answer. This removes the unneeded toArray()
call when flattening each group and uses arrow function syntax. (PHPize Demo)
var_export(
collect($input)
->groupBy('date')
->map(fn($group) => array_merge(...$group))
->values()
->toArray()
);
Or make the whole chain "fully Laravel" with: (PHPize Demo)
var_export(
collect($input)
->groupBy('date')
->map(fn($group) => $group->flatMap(fn($set) => $set))
->values()
->toArray()
);
Upvotes: 0
Reputation: 8078
You can use laravel collection.
First you need to group by groupBy method, then map each group and merge every child array.
Code:
$result = collect($input)
->groupBy('date')
->map(function ($item) {
return array_merge(...$item->toArray());
});
You will get this collection:
And in the end remove the keys(date), you can use values, and simply convert the collection to an array(toArray).
End Code:
$result = collect($input)
->groupBy('date')
->map(function ($item) {
return array_merge(...$item->toArray());
})
->values()
->toArray();
Upvotes: 8