1

I have a multidimensional array with a maximum depth of 4 levels, but not all columns exist in the lower levels and some columns contain subsets instead of primitive values.

I need to calculate the sum of column values.

I wish to get the sum total of [price], [adults] and [children] but have not been able traverse the levels.

The answer I should get with this example is price=380 adults=5 and children=1.

Here is my input array:

[
    8 => [
        2 => [
            'num_rooms' => 2,
            'adults' => [1, 1],
            'children' => [0, 0],
            'prices' => [50, 50],
            'price' => 130,
            'supp' => 30,
        ],
        3 => [
            'num_rooms' => 1,
            'adults' => [1],
            'prices' => [100],
            'price' => 150,
            'supp' => 50,
        ],
    ],
    1 => [
        2 => [
            'num_rooms' => 2,
            'adults' => [1, 1],
            'children' => [1, 0],
            'prices' => [75, 75],
            'price' => 170,
            'supp' => 20,
        ],
    ],
]
2
  • use spl arrayiterator and sum required values. Commented Sep 2, 2011 at 17:14
  • @Keith, This question is made Unclear by your desired result. Summing the prices values is 350. Summing the price values is 450. Your desired result is 380. Commented Jan 14, 2024 at 2:28

5 Answers 5

3

Two loops and a helper array:

$sums = array ( 'price' => 0, 'adults' => 0, 'children' => 0 );

foreach($array as $outer) {
  foreach($outer as $inner) {
    $sums['price'] += $inner['price'];
    $sums['adults'] += array_sum($inner['adults']);
    $sums['children'] += array_sum($inner['children']);
  }
}

print_r($sums);

With a more dynamic version of the inner loop:

foreach($array as $outer) {
  foreach($outer as $inner) {
    foreach($sums as $key => &$v)
      $v += is_array($inner[$key])
        ? array_sum($inner[$key])
        : $inner[$key];
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

is it legal to write $sums['adults'] += $inner['adults'] when $inner['adults'] is an array? i think you need += array_sum( $inner['adults'] ). same for $inner['children'].
Ryan: thanks, good catch! I thought adults was a primitive value. Edited.
yay! although, price is still a primitive; only the other two are arrays
1

This should work:

$price = 0;
$adults = 0;
$children = 0;

foreach($arr as $l1_key => $l1_value)           // iterates over the first level array
{
    foreach($l1_value as $l2_key => $l2_value)  // iterates over second level arrays
    {
         $price += $l2_value['price'];          // add up price totals

         foreach($l2_value['adults'] as $value) // iterate through adults array values
         {
             $adults += $value;                 // sum up adult count
         }

         foreach($l2_value['children'] as $value) // iterate through children array values
         {
             $children += $value;                // sum up children count
         }
    }
}

// now $price, $adults, and $children contain the totals for each

2 Comments

why not just use array_sum( $l2_value['adults'] ) and array_sum( $l2_value['children'] )?
@Ryan - for no specific reason. I was just trying to make it as simple as possible for the OP to understand the basic principles. Of course, in this case, you could have replaced the two inner foreach blocks with array_sum calls to the same end.
0

I didn't test this code but at the same time I don't know how you got 380.. I'm seeing 350?

$sums = getSum($arr);

print_r($sums);

function getSum($arr) {

    $sums = array();
    $sums2 = array();
    $sums['adults'] = 0;
    $sums2['adults'] = 0;
    $sums['children'] = 0;
    $sums2['children'] = 0;
    $sums['prices'] = 0;
    $sums2['prices'] = 0;

    foreach ($arr as $key => $value) {

        $do_not_recurse = false;
        switch ($key) {
            case 'adults':
                $do_not_recurse = true;
                foreach ($value as $adults)
                    $sums['adults'] += $adults;
                break;
            case 'children':
                $do_not_recurse = true;
                foreach ($value as $children)
                    $sums['children'] += $children;
                break;
            case 'prices':
                $do_not_recurse = true;
                foreach ($value as $price)
                    $sums['prices'] += $price;
                break;
            default:
                break;
        }

        if (is_array($value))
            $sums2 = getSum($value);
    }

    $sums['adults'] += $sums2['adults'];
    $sums['children'] += $sums2['children'];
    $sums['prices'] += $sums2['prices'];

    return $sums;
}

2 Comments

Thanks eveybody for your help. Yes 350, sorry. It is stopping at the first array and giving me a price of 150, the total is 170. Array ( [1] => Array ( [2] => Array ( [num_rooms] => 2 [adults] => Array ( [0] => 1 [1] => 1 ) [children] => Array ( [0] => 0 [1] => 0 ) [prices] => Array ( [0] => 75 [1] => 75 ) [price] => 170 [supp] => 20 ) ) ) Warning (2): Invalid argument supplied for foreach() [APP/views/res/addon.ctp, line 70] Array ( [adults] => 2 [children] => 0 [prices] => 150 )
price is the part of the array I am after
0

Handles any depth or array structure and just picks out the terms with the names you are looking for:

function find($term, $array) {
  $count = 0;
  foreach ($array as $item)
    if (is_array($item)) $count += find($term, $item);
  if (isset($array[$term]) {
    if (is_array($array[$term])) $count += array_sum($array[$term]);
    else $count += $array[$term];
  }
  return $count;
}

echo count('price', <the array>);
echo count('adults', <the array>);
echo count('children', <the array>);

5 Comments

The obvious way of doing it... The arrays are not all nested at the same depth, so just do the simple tree-search.
Quite inefficient when you know the (fixed) array structure beforehand.
Inefficient? He's using PHP. Unless he's running this a bazillion times, it's irrelevant whether you make 5 or 20 function calls. It's short, which is efficient to maintain.
PS. Cheers for downvoting because I used recursion.
I wouldn't call that function efficiently to maintain. I had to read it several times to make sure that $count is actually set to the correct values at each call.
0

The first level of your multidimensional array can be safely ignored as irrelevant, so array_merge(...$array) can used to flatten the structure without writing a foreach() and declaring an extra variable.

To iterate the next level and perform totalling on each column dynamically, you can cast each value as an array (for consistency), then add that sum to the previously stored value for that column.

To explain the unconditional casting:

  • When casting a scalar or null value to an array, the value becomes the lone element of the array.
  • When casting an array to be an array, there is no change (because it is already an array).

Code: (Demo)

$result = [];
foreach (array_merge(...$array) as $row) {
    foreach ($row as $k => $v) {
        $result[$k] = ($result[$k] ?? 0) + array_sum((array) $v);
    }
}
var_export($result);

Output:

array (
  'num_rooms' => 5,
  'adults' => 5,
  'children' => 1,
  'prices' => 350,
  'price' => 450,
  'supp' => 100,
)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.