Reputation: 115
I need to group the row data in my 2d array by one column and sum the other columns individually to produce a new 2d array with potentially fewer rows.
In my case, I need to calculate the sums of [num]
and [price]
by each [group]
.
[
['group' => 'Apple', 'num' => 5, 'price' => 10],
['group' => 'Apple', 'num' => 2, 'price' => 8],
['group' => 'Orange', 'num' => 4, 'price' => 6],
['group' => 'Orange', 'num' => 12, 'price' => 24],
]
And the result would be like:
[
['group' => 'Apple', 'num' => 7, 'price' => 18],
['group' => 'Orange', 'num' => 16, 'price' => 30],
]
Upvotes: -1
Views: 768
Reputation: 22817
Using array_reduce()
:
$reduced = array_reduce($array, function(&$result, $item){
$result[$item['group']]['num'] += $item['num'];
$result[$item['group']]['price'] += $item['price'];
return $result;
});
Output: Demo
Warning: {closure}(): Argument #1 ($result) must be passed by reference, value given
Warning: Undefined array key "Apple"
Warning: Undefined array key "num"
Warning: Undefined array key "price"
Warning: {closure}(): Argument #1 ($result) must be passed by reference, value given
Warning: {closure}(): Argument #1 ($result) must be passed by reference, value given
Warning: Undefined array key "Orange"
Warning: Undefined array key "num"
Warning: Undefined array key "price"
Warning: {closure}(): Argument #1 ($result) must be passed by reference, value given
array (
'Apple' =>
array (
'num' => 7,
'price' => 18,
),
'Orange' =>
array (
'num' => 16,
'price' => 30,
),
)
Upvotes: 0
Reputation: 47894
By pushing reference variables into the result array for each unique group, it will not be necessary to re-index the result array after looping.
Code: (Demo)
$result = [];
foreach ($array as $row) {
if (!isset($ref[$row['group']])) {
$ref[$row['group']] = $row;
$result[] =& $ref[$row['group']];
continue;
}
$ref[$row['group']]['num'] += $row['num'];
$ref[$row['group']]['price'] += $row['price'];
}
var_export($result);
As an alternative implementation of @Peter's solution, individual values from each row can be pushed individually into the result array before calling array_values()
(running another interal loop) to re-index the result.
Code: (Demo)
$result = [];
foreach ($array as ['group' => $g, 'num' => $n, 'price' => $p]) {
$result[$g]['group'] = $g;
$result[$g]['num'] = ($result[$g]['num'] ?? 0) + $n;
$result[$g]['price'] = ($result[$g]['price'] ?? 0) + $p;
}
var_export(array_values($result));
Finally, any working implementation with foreach()
loops on this page can be appropriately transcribed to a functional style using array_reduce()
. This is a little more verbose, but eliminates adding adding temporary variables to the global scope.
Code: (Demo)
var_export(
array_values(
array_reduce(
$array,
function ($result, $row) {
if (!isset($result[$row['group']])) {
$result[$row['group']] = $row;
} else {
$result[$row['group']]['num'] += $row['num'];
$result[$row['group']]['price'] += $row['price'];
}
return $result;
}
)
)
);
Upvotes: 0
Reputation: 16923
Simple loop and assocative arrays will do the job:
$result = Array();
foreach($array as $row) {
if(!isset($result[ $row['group'] ])) {
$result[ $row['group'] ] = $row;
continue ;
}
$result[ $row['group'] ]['num'] += $row['num'];
$result[ $row['group'] ]['price'] += $row['price'];
}
$result = array_values($result);
Upvotes: 2