Search code examples
phparrayssumgrouping

Group columnar data from multiple arrays by one array and sum values per group


I'm having this array:

$statuses = ['PROSPECT', 'BACKLOG', 'PROSPECT'];
$of_tranxs = [2, 1, 2];
$revs = [3, 1, 3];
$mgps = [4, 1, 4];

And I want sum by duplicate status. output an array like this:

array(
  'status' => ['PROSPECT', 'BACKLOG'],
  'of_tranx' => [4, 1],
  'rev' => [6, 1],
  'mgp' => [8, 1]
)

I'm using PHP 7.4

I have tried with the following:

$result = array_zip_combine(
    ['status', 'of_tranx', 'rev', 'mgp'], 
    $statuses, $of_tranxs, $revs, $mgps
);

then I foreach but the result is not what I want.


Solution

  • You can perform the summing in the original arrays, then re-index and merge them after executing a single loop. I expect this is rather efficient and I think it will be easy to read and maintain.

    Code: (Demo)

    foreach ($statuses as $index => $status) {
        if (!isset($found[$status])) {
            $found[$status] = $index;
            continue;
        }
        $of_tranxs[$found[$status]] += $of_tranxs[$index];
        $revs[$found[$status]] += $revs[$index];
        $mgps[$found[$status]] += $mgps[$index];
        unset($statuses[$index], $of_tranxs[$index], $revs[$index], $mgps[$index]);
    }
    var_export([
        'status' => array_values($statuses),
        'of_tranx' => array_values($of_tranxs),
        'rev' => array_values($revs),
        'mgp' => array_values($mgps)
    ]);
    

    Or perhaps simpler by maintaining a new index incrementing variable: (Demo)

    $result = [];
    $i = 0;
    foreach ($statuses as $oldIndex => $status) {
        if (!isset($newIndex[$status])) {
            $newIndex[$status] = $i++;
            $result['status'][] = $status;
            $result['of_tranx'][] = $of_tranxs[$oldIndex];
            $result['rev'][] = $revs[$oldIndex];
            $result['mgp'][] = $mgps[$oldIndex];
        } else {
            $result['of_tranx'][$newIndex[$status]] += $of_tranxs[$oldIndex];
            $result['rev'][$newIndex[$status]] += $revs[$oldIndex];
            $result['mgp'][$newIndex[$status]] += $mgps[$oldIndex];
        }
    }
    var_export($result);
    

    Finally, if you don't want to keep track of indexes in the result array AND you don't want to perform another four loop with array_values() x4, then you can push reference variables into the result array.

    Code: (Demo)

    $result = [];
    foreach ($statuses as $i => $status) {
        if (!isset($ref[$status])) {
            $ref[$status] = [
                'status' => $status,
                'of_tranxs' => $of_tranxs[$i],
                'revs' => $revs[$i],
                'mgp' => $mgps[$i],
            ];
            $result[] = &$ref[$status];
        } else {
            $ref[$status]['of_tranxs'] += $of_tranxs[$i];
            $ref[$status]['revs'] += $revs[$i];
            $ref[$status]['mgp'] += $mgps[$i];
        }
    }
    var_export($result);