Search code examples
phparraystransposecumulative-sumarray-sum

Calculate totals for each column of a two dimensional array (matrix)


Given the array below, how can I create a sum array with matching keys?

$arr = [
    ['alpha', 1, 2, 3, 4, 5],
    ['beta', 1, 2, 3, 4, 5],
    ['gamma', 1, 2, 3, 4, 5],
    ['delta', 1, 2, 3, 4, 5]
];

This is what I eventually want:

['', 4, 8, 12, 16, 20]

Is this the most efficient way?

function getArrTotals($arr) {
    $arrTotal = array();
    foreach ($arr as $subArr) {
        foreach ($subArr as $k => $v) {
            if (is_numeric($v)) {
                if (!array_key_exists($k,$arrTotal)) {
                    $arrTotal[$k] = 0;
                }
                $arrTotal[$k] = $arrTotal[$k] + $v;
            } else {
                if (!array_key_exists($k,$arrTotal)) {
                    $arrTotal[$k] = '';
                }
            }
        }
    }
    return $arrTotal;
}

Solution

  • Using the new array_column() function of PHP 5.5:

    $colCount = count($arr[0]);
    $result = array();
    for ($i = 0; $i < $colCount; $i++) {
        $result[] = 
            array_sum(
            array_column($arr, $i)
        );
    }
    var_dump($result);
    

    or for earlier versions of PHP

    $result = array();
    foreach(call_user_func_array('array_map',array_merge(array(NULL),$arr)) as $column) {
        $result[] = array_sum($column);
    }
    var_dump($result);
    

    Both methods assume the same number of entries in each sub-array

    EDIT

    Filtering out the non numeric values to return an empty string for the first column:

    $colCount = count($arr[0]);
    $result = array();
    for ($i = 0; $i < $colCount; $i++) {
        $values = array_filter(
            array_column($arr, $i),
            'is_numeric'
        );
        $result[] = count($values) > 0 ? array_sum($values) : '';
    }
    var_dump($result);
    

    and

    $result = array();
    foreach(call_user_func_array('array_map',array_merge(array(NULL),$arr)) as $column) {
        $values = array_filter($column,
            'is_numeric'
        );
        $result[] = count($values) > 0 ? array_sum($values) : '';
    }
    var_dump($result);
    

    If you want a null instead, then simply replace the '' with null