1

I'm using PHP 5.6, and I have the following array:

Array (
    [0] => Array (
            [id] => 1
            [name] => James
        )
    [1] => Array (
            [id] => 2
            [name] => Tim
            [children] => Array (
                    [0] => Array (
                            [id] => 4
                            [name] => Sam
                        )
                    [1] => Array (
                            [id] => 5
                            [name] => Florence
                        )
                )
        )
    [2] => Array (
            [id] => 3
            [name] => Stephen
        )
)

I'm trying to find a neat and fast way to count the number of people, which is the same as counting the number of numeric keys, which should be 5.

echo count($myarray); // 3 (only first level)
echo count($myarray, COUNT_RECURSIVE); // 16 (every key/value)

Is there a good way to do this with built-in PHP functions, or do I need to traverse the whole multidimensional array and count them manually..?

EDIT My array could end up being 1,000+ people (or more), with many many levels (an unknown number of levels).

5
  • Is there a reason that you can't just walk through it with a for loop? Commented Jan 13, 2016 at 14:49
  • @kainaw, a for loop would have to recurs through the array many time, like the function in my answer :P Commented Jan 13, 2016 at 14:50
  • I could, but my array could end up being 1,000+ people, with many many levels (an unknown number of levels), so I'm looking for a neater way. Commented Jan 13, 2016 at 14:52
  • @CrusaderLtd, has my answer helped you at all or do you need something else? Commented Jan 13, 2016 at 14:56
  • count() just counts keys. it doesn't care WHAT those keys are, or they represent. it has no filtering functions. if you want to count only numeric keys, you'll have to build that yourself. e.g. preg_grep('/^\d+$/', array_keys($arr)), but that doesn't do recursion. Commented Jan 13, 2016 at 15:05

2 Answers 2

2

It is important to note that, even if there were a PHP built-in (such as count($myarray, COUNT_RECURSIVE_NUMERIC);) internally, it would still be traversing the whole array, recursively. If you are worried about Out Of Memory errors, try pass-by-reference, which will not copy the array or the array items:

define('COUNT_RECURSIVE', 1);

function count_numeric_keys(&$array, $flags = 0) {
    $count = 0;
    foreach ($array as $key => $value) {
        $count += (int) (is_numeric($key));
        if ($flags & COUNT_RECURSIVE && is_array($value)) {
            $count += count_numeric_keys($value, $flags);
        }
    }
    return (int) $count;
}

$count = count_numeric_keys($array, COUNT_RECURSIVE);

Mayhaps?

Comparison with non-pass-by-reference, type-hint, and small benchmark:

define('COUNT_RECURSIVE', 1);

function count_numeric_keys(Array &$array, $flags = 0) {
    $count = 0;
    foreach ($array as $key => $value) {
        $count += (int) (is_numeric($key));
        if ($flags & COUNT_RECURSIVE && is_array($value)) {
            $count += count_numeric_keys($value, $flags);
        }
    }
    return (int) $count;
}

function count_numeric_keys_np(Array $array, $flags = 0) {
    $count = 0;
    foreach ($array as $key => $value) {
        $count += (int) (is_numeric($key));
        if ($flags & COUNT_RECURSIVE && is_array($value)) {
            $count += count_numeric_keys_np($value, $flags);
        }
    }
    return (int) $count;
}

$tpl_array = array(
    1=>"one",
    "two"=>"two",
    3=>array(
        1=>1,
        "two"=>2
    )
);

// Fill the array with both numeric and non-numeric
$array = array();
for($i = 1000; $i > 0; $i--) {
    $array[] = $tpl_array;
}

for($i = 1000; $i > 0; $i--) {
    $array["not a number $i"] = $tpl_array;
}

echo "Pre Memory: ".memory_get_usage(TRUE).PHP_EOL;
echo "Count: ".count_numeric_keys($array, COUNT_RECURSIVE).PHP_EOL;
echo "Reference Memory: ".memory_get_usage(TRUE)." current, ".memory_get_peak_usage(TRUE)." peak.\n";

count_numeric_keys_np($array, COUNT_RECURSIVE);
echo "No-Reference Memory: ".memory_get_usage(TRUE)." current, ".memory_get_peak_usage(TRUE)." peak.\n";

View it on IDEONE here.

ODDLY, having a reference on $value, like foreach($array as $key => &value) {} actually increased memory usage. Bizarre...

Sign up to request clarification or add additional context in comments.

4 Comments

pass by ref.. you should do the same with the $count var and just do $count++;
Thanks Mike... Any mileage is using array_walk rather than foreach..?
Less overhead in foreach, array_walk is cleaner for applying strictly callbacks, also, it would prevent the counting bit without additional overhead.
Just a note, I re-edited my edit. The Flag adds minimal overhead, and I added a type-hint. IDEOne is up to date.
1

I just created this recursive function to do this for ya :P

function countNumericKeys($array)
{
    $count = 0;
    foreach ($array as $key => $value)
    {
        if (is_numeric($key))
        {
            $count ++;
        }
        if (is_array($value))
        {
            $count += countNumericKeys($value);
        }
    }
    return $count;
}

// Test it!

$array = [
    1=>"one",
    "two"=>"two",
    3=>[
        1=>1,
        "two"=>2
    ]
];

print countNumericKeys($array); // Output: 3, correct (in this case at least)!

Shortened the code so it uses ternary operators instead of the ifs that it was :P

function simpleCountNumericKeys($array)
{
    $count = 0;
    foreach ($array as $key => $value)
    {
        $count += is_numeric($key) ? 1 : 0;
        $count += is_array($value) ? simpleCountNumericKeys($value) : 0;
    }
    return $count;
}

TEST USING array_keys -- only gets the top level keys of the array

function countArrayKeysNumeric($array)
{
    $count = 0;
    $keys = array_keys($array);
    foreach ($keys as $key) $count += is_numeric($key) ? 1 :0;
    return $count;
}

$array = [
    1=>"one",
    "two"=>"two",
    3=>[
        1=>1,
        "two"=>2
    ]
];

print countArrayKeysNumeric($array);

Prints 2... :(

10 Comments

Thanks Sam, that obviously works, however I was looking for a different way (other than traversing the whole array). I guess count($myarray, COUNT_RECURSIVE_NUMERIC); would be perfect, but doesn't exist.
@CrusaderLtd, as far as I know, there (as you say), isn't a way to do this in PHP by default, so a custom wrapper which iterates through the array is the best way (I'm sure PHP would do the same thing)
@CrusaderLtd, the other way would be to use array_keys and loop through that, but that again is not recursive through all the parts (AFAIK), see the test at the bottom of my answer
@CrusaderLtd, may I ask why you do not want to iterate through the whole array? (Seems like a bad idea because without traversal/iteration you are not going to get all the arrays that are nested counted)
@SamSwift웃 Of course, you took the recursion out, I'll use a recursive function with ternary operators. :-)
|

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.