Home > Back-end >  Sum PHP multidimensional array by columns
Sum PHP multidimensional array by columns

Time:03-28

I have an array to be summed by column(s)

Array (
    0 => [
        [category] => Tools, furniture & fixtures
        [period] => 2022-07
        [depreciation] => 100
    ],
    1 => [
        [category] => Tools, furniture & fixtures
        [period] => 2022-07
        [depreciation] => 50
    ],
    2 => [
        [category] => Machines
        [period] => 2022-07
        [depreciation] => 25
    ],
    3 => [
        [category] => Machines
        [period] => 2022-07
        [depreciation] => 75
    ],
    4 => [
        [category] => Machines
        [period] => 2022-08
        [depreciation] => 200
    ]
)

I want to sum depreciation column by combination of category and period column, something like :

Array (
    0 => [
        [category] => Tools, furniture & fixtures
        [period] => 2022-07
        [depreciation] => 150
    ],
    1 => [
        [category] => Machines
        [period] => 2022-07
        [depreciation] => 100
    ],
    2 => [
        [category] => Machines
        [period] => 2022-08
        [depreciation] => 200
    ]
)

I tried to used to utilize this function to do this IF only one column as the parameter/key, and no idea if it have to be more than one column (category and period) :

$itemsSummed = [];
foreach ($depreciations as $item) {
    if (array_key_exists($item['category'], $itemsSummed)) {
        $itemsSummed[$item['category']]['depreciation']  = $item['depreciation'];
    } else {
        $itemsSummed[$item['category']] = $item;
    }
}

$itemsSummed = array_values($itemsSummed);

How can i achieve this ?

CodePudding user response:

The easiest way to do that is pooling the sums together based on unique keys, which in your case are concatenated from "category" and "period". First things first, your data:

$data = [
    0 => [
        'category' => 'Tools, furniture & fixtures',
        'period' => '2022-07',
        'depreciation' => '100'
    ],
    1 => [
        'category' => 'Tools, furniture & fixtures',
        'period' => '2022-07',
        'depreciation' => 50
    ],
    2 => [
        'category' => 'Machines',
        'period' => '2022-07',
        'depreciation' => 25
    ],
    3 => [
        'category' => 'Machines',
        'period' => '2022-07',
        'depreciation' => 75
    ],
    4 => [
        'category' => 'Machines',
        'period' => '2022-08',
        'depreciation' => '200'
    ]
];

Then let's iterate your data:

$sums = [];

foreach($data as $vals) {

    // Concatenate category period for unique grouping key:
    $sum_key = "{$vals['category']}__{$vals['period']}";

    // Set sum to zero if not defined yet:
    $sums[$sum_key] ??= 0;

    // Add depreciation
    $sums[$sum_key]  = $vals['depreciation'];
}

This would give you the following:

array(3) {
    ["Tools, furniture & fixtures__2022-07"] · int(150)
    ["Machines__2022-07"] · int(100)
    ["Machines__2022-08"] · int(200)
}

That's the basic logic. To model the output in the form of your source array, we'll follow the logic above with the difference that in place of ??= 0, we initialize the "sum" item with the values common to each group, and then add to the "depreciation" value at each iteration:

foreach($data as $vals) {

    // Concatenate category period for unique grouping key:
    $sum_key = "{$vals['category']}__{$vals['period']}";
    
    // Initialize the grouped array item with zero "summable":
    $sums[$sum_key] ??= [
        'category' => $vals['category'],
        'period' => $vals['period'],
        'depreciation' => 0,
    ];
    
    // Add depreciation:
    $sums[$sum_key]['depreciation']  = $vals['depreciation'];
}

This would give you:

array(3) {
    ["Tools, furniture & fixtures__2022-07"] · array(3) {
        ["category"] · string(27) "Tools, furniture & fixtures"
        ["period"] · string(7) "2022-07"
        ["depreciation"] · int(150)
    }
    ["Machines__2022-07"] · array(3) {
        ["category"] · string(8) "Machines"
        ["period"] · string(7) "2022-07"
        ["depreciation"] · int(100)
    }
    ["Machines__2022-08"] · array(3) {
        ["category"] · string(8) "Machines"
        ["period"] · string(7) "2022-08"
        ["depreciation"] · int(200)
    }
}

If the grouping keys look awkward, or if you just want a numerically indexed array per your sample outcome, array_values($sums) in the end will "clean it up". I've left it as is to highlight the logic of using a "unique grouping key" to aggregate values.

CodePudding user response:

$list = [
    [ 'category' => 'Tools', 'period' => '2022 - 07', 'depreciation' => 100 ],
    [ 'category' => 'Tools', 'period' => '2022 - 07', 'depreciation' => 50 ],
    [ 'category' => 'Machines', 'period' => '2022 - 07', 'depreciation' => 25 ],
    [ 'category' => 'Machines', 'period' => '2022 - 07', 'depreciation' => 75 ],
    [ 'category' => 'Machines', 'period' => '2022 - 08', 'depreciation' => 200 ]
];

$result = array_reduce(
    $list,
    function ($carry, $item) {
      if (is_array($carry)) {
        foreach ($carry as &$value) {
          if ($value['category'] === $item['category'] && $value['period'] === $item['period']) {
            $value['depreciation']  = $item['depreciation'];
            return $carry;
          }
        }
        return [ ...$carry, $item ];
      }
      return $item;
    },
    []
);

print_r($result);
  • Related