Search code examples
phparraysmultidimensional-arraysumgrouping

Group array rows by multiple column values and sum another column within each group


I'm trying to group the data in an array of associative arrays by three columns (year, month, grupo) and sum another column (quantity).

Given an array like:

$in = [
    ['year' => '2010', 'month' => '11', 'grupo' => '2', 'quantity' => 3],
    ['year' => '2010', 'month' => '11', 'grupo' => '3', 'quantity' => 4],
    ['year' => '2011', 'month' => '2', 'grupo' => '2', 'quantity' => 4],
    ['year' => '2011', 'month' => '2', 'grupo' => '2', 'quantity' => 4],
    ['year' => '2012', 'month' => '3', 'grupo' => '4', 'quantity' => 3]
    ['year' => '2012', 'month' => '3', 'grupo' => '4', 'quantity' => 3]
];

I want get:

[
    ['year' => '2010', 'month' => '11', 'grupo' => '2', 'quantity' => 3],
    ['year' => '2010', 'month' => '11', 'grupo' => '3', 'quantity' => 4],
    ['year' => '2011', 'month' => '2', 'grupo' => '2', 'quantity' => 8],
    ['year' => '2012', 'month' => '3', 'grupo' => '4', 'quantity' => 6]
]

I've tried something like this:

$out = array();
foreach ($in as $row) {
    if (!isset($out[$row['year']['month']['grupo']])) {
        $out[$row['year']['month']['grupo']] = array(
            'year' => $row['year'],
            'month' => $row['month'],
            'grupo' => $row['grupo'],
            'quantity' => 0,
        );
    }
    $out[$row['year']['month']['grupo']]['quantity'] += $row['quantity'];
}
$out = array_values($out);

but it fails while trying to group the 3 fields.


Solution

  • I think the earlier answers overlooked the desired result -- the solutions from @hellcode and @Rasclatt create unnecessarily deep structures.

    The coding attempt in the question was very close to being right, there was only a flaw in generating unique grouping keys in the first level.

    To generate unique grouping keys, you must forge a string from the multiple identifying values in the row. In other words, "compose" a unique value from the three targeted column values -- a composite key.

    Code: (Demo)

    $result = [];
    foreach ($in as $row) {
        $compositeKey = implode(array_slice($row, 0, 3));
        if (!isset($result[$compositeKey])) {
            $result[$compositeKey] = $row;
        } else {
            $result[$compositeKey]['quantity'] += $row['quantity'];
        }
    }
    var_export(
        array_values($result)
    );