3

I have need to calculate the sum of the deep elements foreach first level element.

Sample 3-level array:

[
    1 => [
        'A' => ['AA' => 3, 'AB' => 5],
        'B' => ['BA' => 2]
    ],
    2 => [
        'C' => ['CA' => 4],
        'D' => ['DA' => 1, 'DB' => 2]
    ],
    3 => [
        'E' => ['EA' => 1, 'EB' => 2, 'EC' => 3],
        'F' => ['FA' => 0, 'FB' => 7, 'FC' => 7]
    ]
]

I want to sum the values and this is my expectation:

Array(
    [1] => 10        
    [2] => 7
    [3] => 20
)

Here is my code that I used for summing the value:

$total[$country_id][$province_id][$city_id] = $amount;

$result = array();
foreach( $total as $key => $val ){
         $total[$key] = array_sum ( $val );
}

Can someone explain what is wrong with my code or explain how foreach work? The result of my code is 0 and actually I just studied around 1 week about foreach.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
payket
  • 137
  • 7

5 Answers5

3

As you want to know more about foreach, here is a more verbose solution using it :

$total = 0;
$totalByCountry = [];

// iterate over each country
foreach ($arr as $countryId => $provinces) {

    $totalByCountry[$countryId] = 0;

    // iterate over each province
    foreach ($provinces as $provinceId => $cities) {

        // iterate over each city
        foreach ($cities as $cityId => $value) {
            $totalByCountry[$countryId] += $value;
            $total += $value;
        }
    }
}

Result of var_dump(totalByCountry) :

array (size=3)
    1 => int 10
    2 => int 7
    3 => int 20

Result of var_dump($total) :

int 37

-- edit --

In real world project, you better be less verbose and use php functions made for this kind of situation like array_walk_recursive(), as in Philipp Maurer and Firoz Ahmad answers.

micster
  • 758
  • 3
  • 10
  • You use for loop again and again. If another dimension is added with the array then you need to use for loop manually and again. – Firoz Ahmad Likhon Mar 20 '18 at 11:29
  • You're right, it was just an example to show how to use `foreach` as he asked, not the best solution. I'd use `array_walk_recursive` too in real project. I'll update my answer with this solution but there is already other solutions given here. – micster Mar 20 '18 at 11:45
1

The problem with your foreach is, that your array is three layers deep, while your foreach only traverses the first and array_sum() only calculates the second layer.

An example of your foreach:

$total = [
    0 => [
        'A' => [
            'AA' => 1,
            'AB' => 2
        ],
        'B' => [
            'BA' => 3,
            'BB' => 4
        ]
    ]
];

foreach( $total as $key => $val ){
    $total[$key] = array_sum ( $val );
}

The foreach will iterate over the $total array. This is the iteraton for the example:

  1. $key = 0; $value = ['A'=>['AA'=>1,'AB'=>2], 'B'=>['BA'=>3,'BB'=>4]]
    In your loop, you then call array_sum() on $value. array_sum() adds the children of the provided array together, if they are numeric. Sadly $value is a two dimensional array and its children are arrays, so they are not numeric. That is why 0 is returned for it.

A solution to your problem would be to use the array_walk_recursive() function, that traverses inner arrays and calls a function upon them, that you can specify:

$sum = 0;
array_walk_recursive($total, function($value) use (&$sum) {
    $sum += $value;
});

The specified function adds the value of each leaf in your array to the specified variable $sum, that is given by reference to the function.

Philipp Maurer
  • 2,480
  • 6
  • 18
  • 25
  • I tried your code and got parse error on this line : array_walk_recursive($total, function($value) use (&$sum) – payket Mar 20 '18 at 10:31
  • @payket What kind of parse error? :-) Also check this [running example](http://sandbox.onlinephpfunctions.com/code/a3995e589d4c4fdd53efc2918d4c0771948e642c) of my code to see how its implemented :-) – Philipp Maurer Mar 20 '18 at 10:35
  • I guess $key is the problem, cause in my editor it show comment "unused parameter key". could you explain more about array_walk_recursive to me? – payket Mar 21 '18 at 03:31
0

print the $total,When coming to the foreach, $val is an array. so when calling array_sum function, it sums the first array and assign the total to a new array's first element. That is how this is working. The reason why you code is not working, there should be a foreach outside the foreach that you have been provided. Remove this foreach from above foreach's scope.

<?php

$x=[0=>[1,2,3],1=>[2,3,4]];

$result = array();
foreach( $x as $key => $val ){
       $total[$key] = array_sum ( $val );
}
var_dump($total);
?>

If your array is 3 dimentional Code is like this,

$y=[0=>[0=>[1,2,3],1=>[3,4,5]],1=>[0=>[5,6,7],1=>[8,9,10]]];

$result = array();
foreach( $y as $vals ){
   foreach( $vals as $key => $val ){
        $total[$key] = array_sum ( $val );
   }
}
var_dump($total);
Viraj Amarasinghe
  • 911
  • 10
  • 20
  • I need $total[$country_id][$province_id][$city_id] = $amount to declare the amount of the country. my code working well when the $total is 2 dimensional array ($total[$province_id][$city_id] = $amount) – payket Mar 20 '18 at 10:33
  • I modified the answer, now check – Viraj Amarasinghe Mar 20 '18 at 10:40
0

To achieve your desired output, you at first should iterate over your array and check if there exists any sub array then, again traverse all array members by using array_walk_recursive() function

<?php

    $arr = [
        [
            'A' => [
                'AA' => 3,
                'BB' => 5,
                'CC' => [
                    'DD' => 4,
                    'EE' => 5
                ]
            ],
            'B' => [
                'BA' => 2
            ]
        ],
        [
            'C' => [
                'CC' => 3,
                'CCC' => 2,
            ],
            'D' => [
                'DD' => 2
            ]
        ],

        5,
        [
            2,
            7
        ]

    ];

    $arrSum = [];

    $count = count($arr);

    for ($i = 0; $i < $count; $i++) {
        $sum = 0;
        if(is_array($arr[$i])) {
            array_walk_recursive($arr[$i], function ($item, $key) use (&$sum) {
                $sum += $item;
            });
        }
        else {
            $sum += $arr[$i];
        }

        $arrSum [] = $sum;
        $sum = 0;
    }

    var_dump($arrSum);
0

For a functional-style solution, use array_map() to iterate the first level's data sets.

Then flatten the remaining levels in preparation for array_sum().

array_values() is currently necessary because PHP does not yet allow the unpacking of non-numerically keyed arrays.

array_merge(...$variable) acts to flatten the 2d structure.

Code: (Demo)

var_export(
    array_map(
        fn($set) => array_sum(array_merge(...array_values($set))),
        $array
    )
);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136