views:

165

answers:

8

Hello. I have an array like this:

Array
(
    [1000] => Array
        (
            [pv] => 36
        )

    [1101] => Array
        (
            [1102] => Array
                (
                    [pv] => 92
                )

            [pv] => 38
        )

    [pv] => 64
)

How I can find the sum of all array elements with key 'pv', regardless of the depth at which they appear.

For this example the result will 36+92+38+64 = 240

Thanks for help.

A: 
private function calculateUserGv($userId) {
    $group = $this->arrayUnder($this->_user_tree, $userId);
    global $gv;
    $gv = 0;
    $gv    = $this->gvRecursive($group);
    return;
}

private function gvRecursive($group) {
    global $gv;
    foreach ($group as $key => $val) {
        if ($key == 'pv') {
            $gv += $group[$key];
        }
        else {
            $this->gvRecursive($val);
        }
    }
    return $gv;
}
Alexander.Plutov
having `global $gv`, modifying it within the function, then `return $gv` is not nice. Invoking this function with `$gv = $this->gvRecursive($group);` is not nice at all.
fredley
+5  A: 
function addPV($array){
  $sum = 0;
  foreach($array as $key => $a){
    if (is_array($a)){
       $sum += addPV($a);
    }else if($key == 'pv') {
       $sum += $a;
    }
  }
  return $sum;
}
fredley
+1! I hate passing function names as string as required for `array_reduce()`, etc. Nice and clean solution!
elusive
@elusive: in PHP5.3 you can just pass anonymous functions (like javascript tends to do), maybe that's less creepy to you: `$arr = range(1,40); var_dump(array_reduce($arr,function($v,$k){return $v+$k;}));`
Wrikken
@Wrikken: Thats right, but you cannot assume that PHP 5.3 is already running everywhere. AFAIK, 5.3 will ship with the next release of debian at the earliest. fredleys solution works nicely with currently used versions of PHP and i consider it less ugly than the `array_reduce()` method.
elusive
Afaik, 5.3.2 is in the testing branch, so in Debian terms that will be 'soonish', and I'm not disputing this is a nice solution (+1 'ed it), just reacted to your _'I don't like to pass function names as string'_ with some info you might be happy with in future in case you didn't know already ;)
Wrikken
@Wrikken: Yes, thanks ;)
elusive
+1  A: 
$sum = 0;

function sumArray($item, $key, &$sum)
{
    if ($key == 'pv')
       $sum += $item;
}

array_walk_recursive($array, 'sumArray',&$sum);
echo $sum;
Mark Baker
Works as long as there are nothing other than arrays and numbers with the key `pv`.
fredley
@fredley - if the OP has additional criteria (eg test for booleans, etc) they can easily be added to the sumArray callback function
Mark Baker
+4  A: 

you can use array_reduce or array_walk_recursive functions and a custom call back function of yourself:

function sum($v, $w)
{
    $v += $w;
    return $v;
}

$total = array_reduce($your_array, "sum");
Ali Sattari
+13  A: 

Another alternative:

$sum = 0;
$array_obj = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($array_obj as $key => $value) {
    if($key == 'pv')
        $sum += $value;
}
echo $sum;

Update: Just thought I'd mention that this method uses the PHP SPL Iterators.


Salathe Edit:

A simple (relatively) way to filter the keys and to sum the values (without writing a custom Iterator) would be to do some filtering with a RegexIterator, convert the resulting iterator into an array and use the handy array_sum function on it. This is purely an academic exercise and I certainly wouldn't advocate it as the best way to achieve this... however, it is just one line-of-code. :)

$sum = array_sum(
    iterator_to_array(
        new RegexIterator(
            new RecursiveIteratorIterator(
                new RecursiveArrayIterator($array)
            ),
            '/^pv$/D',
            RegexIterator::MATCH,
            RegexIterator::USE_KEY
        ),
        false
    )
);
Russell Dias
+1! This one is nice, too! It does not require an extra function and does not clutter up the namespace. I like it!
elusive
+1 for IteratorIterator I recently used this for directory traversing to find certain files with the RegexIteratorIterator.
Chris
+1: Plus, you could even toss in a FilterIterator and not have to worry about the `if($key == 'pv')``part... But this is on the mark either way...
ircmaxell
+1 A powerful use of the SPL library. Bravo for demonstrating a more functional approach. I'd love to also see ircmaxell's suggestion plus array_reduce() implemented; That would completely remove the loop.
erisco
@ircmaxell,@erisco Cue ridiculous one-liner... `$sum = array_sum(iterator_to_array(new RegexIterator(new RecursiveIteratorIterator(new RecursiveArrayIterator($array)), '/^pv$/D', RegexIterator::MATCH,RegexIterator::USE_KEY), false));`
salathe
Yeah, that's ridiculous. An awesome display of what you can do with SPL, but still ridiculous.
ircmaxell
The term one-liner seems to be used very loosely these days... But nice solution nonetheless. +1 to you, oh wait...
Russell Dias
+3  A: 

based on @Ali Sattari answer

function sum($v, $w) {
    return $v + (is_array($w) ? 
        array_reduce($w, __FUNCTION__) : $w);
}
stereofrog
+1  A: 
$array = array('1000'=> array('pv'=>36), array('1101' => array('pv'=>92)));

$total = 0;
foreach(new recursiveIteratorIterator( new recursiveArrayIterator($array)) as $sub)
{
 $total += (int)  $sub;
}
print_r($total);
Auirio
+1  A: 
function SumRecursiveByKey($array,$key)
{
    $total = 0;
    foreach($array as $_key => $value)
    {
        if(is_array($value))
        {
            $total += SumRecursiveByKey($array,$key);
        }elseif($_key == $key)
        {
             $total += $value;
        }
    }
    return $total;
}

use like

$summed_items = SumRecursiveByKey($myArray,'pv');

This would give you more leeway in checking alternative keys a swell.

RobertPitt