I have been reading about collections in Laravel. Currently I am aware of how to use it however I have a question about the sum()
function.
According to the docs it can be used like this:
collect([1, 2, 3, 4, 5])->sum();
And also like this:
$collection = collect([
['name' => 'JavaScript: The Good Parts', 'pages' => 176],
['name' => 'JavaScript: The Definitive Guide', 'pages' => 1096],
]);
$collection->sum('pages');
Which is fine and I understand, however now I have an array with nested objects and I would like to know how I can achieve something like this in laravel:
$collection = collect([
['name' => 'JavaScript: The Good Parts', 'pages' => 176, 'price' => 100.00],
['name' => 'JavaScript: The Definitive Guide', 'pages' => price, 'pages' => 150.00],
]);
$collection->sum(['pages', 'prices']);
Which would obviously return either an array like this:
[1272, 250]
or on collection / object like this:
{"total_pages": 1272, "total_price": 250}
in short I would like to know how I can sum and return the results of multiple columns in a laravel collection instead of doing this twice:
$collection->sum('pages');
$collection->sum('price');
Since i assume that would cause multiple loops over the collection or am I wrong?
You would need to use the pipe
method to pass the collection to a callback. Within the callback you could achieve what you're wanting to do.
$collection = collect([
['name' => 'JavaScript: The Good Parts', 'pages' => 176, 'price' => 100.00],
['name' => 'JavaScript: The Definitive Guide', 'pages' => 314, 'price' => 150.00]
]);
$result = $collection->pipe(function ($collection) {
return collect([
'total_pages' => $collection->sum('pages'),
'total_price' => $collection->sum('price'),
]);
});
Looking at the code behind the sum() function
, we can see that internally $collection->reduce()
is used to collect the data. So we should be able to use the same code to achieve what you are looking for:
return $collection->reduce(function ($result, $item) {
if ($result === null) {
$result = [
'pages' => 0,
'price' => 0,
];
}
$result['pages'] += $item['pages'];
$result['price'] += $item['price'];
return $result;
}, 0);
To get a more generic solution out of it, you could also create a macro on the Collection
class within one of your service providers:
Collection::macro('sumMultiple', function ($columns) {
return $this->reduce(function ($result, $item) use ($columns) {
if ($result === null) {
foreach ($columns as $column) {
$result[$column] = 0;
}
}
foreach ($columns as $column) {
$result[$column] += $item[$column];
}
return $result;
}, 0);
});
You can then use the new function with $collection->sumMultiple(['pages', 'price'])
and you will receive a result like ['pages' => 123, 'price' => 234]
.
Pleas be aware that sumMultiple
does not support nested arrays at this moment (i.e. dot notation access to fields does not work).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With