4

I have the following code, and I'd like to get away from the call-time pass-by-reference, (To convert from 5.2 to 5.3) but I'm not sure exactly sure what the correct way to do this would be (class, global variable, ?)

Here is a codepad that should have everything in it http://codepad.org/ombgFPMR

<?php

function count_things($item, $key, $total) {
    $total++;
}

$counts = array(100 => 1,
            101 => 1,
            102 => array(
                106 => 1,
                107 => 1
            ),
            103 => 1,
            104 => 1,
            105 => array(
                108 => 1,
                109 => array(
                    110 => 1,
                    111 => 1,
                    112 => 1
                )
            )
        );

foreach($counts as $key => $count) {
    $total = 0;

    if(is_array($count)) {
            $total++;
            /* The below is a logic error. Array elements that contain arrays do not get
               the callback function called on them. Therefore, any children with children
               of their own will not be counted. In the output of this paste,
               the final key, $final_counts[105]['total'], should have a value of 6, but it
               actually has a value of 5. */
    array_walk_recursive($count, 'count_things', &$total);
} else {
            $total = $count;
    }

    $final_counts[$key]['total'] = $total;
}   

print_r($final_counts);

?>

Output looks like:

Array
(
[100] => Array
    (
        [total] => 1
    )

[101] => Array
    (
        [total] => 1
    )

[102] => Array
    (
        [total] => 3
    )

[103] => Array
    (
        [total] => 1
    )

[104] => Array
    (
        [total] => 1
    )

[105] => Array
    (
        [total] => 5
    )

)
merlincam
  • 1,818
  • 3
  • 16
  • 16

1 Answers1

3

You can use count with COUNT_RECURSIVE flag.

You should use closures for this, these were introduced in 5.3.0 so they should work.

<?php

$counts = array(
    100 => 1,
    101 => 1,
    102 => array(
        106 => 1,
        107 => 1
    ),
    103 => 1,
    104 => 1,
    105 => array(
        108 => 1,
        109 => array(
            110 => 1,
            111 => 1,
            112 => 1
        )
    )
);

$final_counts = array();

foreach($counts as $key => $count) {

    if(is_array($count)) {
        $total = 1;
        array_walk_recursive($count, function() use (&$total) {
            $total++;
        });
    } else {
        $total = $count;
    }

    $final_counts[$key]['total'] = $total;
}

print_r($final_counts);

I might be able to provide a better solution if you put your problem in context.

Cameron Martin
  • 5,952
  • 2
  • 40
  • 53
  • Nice idea, but it's producing a different count for key `105` (`6` instead of `5`). – bfavaretto Jun 06 '12 at 21:53
  • 1
    @bfavaretto: Pretty sure it's supposed to actually be `6` (meaning the OP's code isn't right). If `102` is `3`, then `105` should be `6`. – gen_Eric Jun 06 '12 at 21:54
  • @bfavaretto: That doesn't make the OP's algorithm correct :-P – gen_Eric Jun 06 '12 at 21:58
  • Yeah sorry this is wrong. Count with recursive flag counts all arrays plus its elements, not just the elements. I've updated my post to use closures. – Cameron Martin Jun 06 '12 at 21:59
  • @ddlshack: +1 Nice job w/ the closures! – gen_Eric Jun 06 '12 at 22:00
  • This is the OP's coworker. When he originally posted it, he helpfully deleted my code comment (which he has now penitently added back) that explained that there was indeed a logic error and the output was wrong. The count for `105` should indeed have been 6, and `count` with `COUNT_RECURSIVE` does just fine. I apologize for the trouble; I wrote the original code, but IANAP and this code should never have seen the light of day outside our office. – 75th Trombone Jun 06 '12 at 22:45