views:

2341

answers:

8

A PHP array can have arrays for its elements. And those arrays can have arrays and so on and so forth. Is there a way to find out the maximum nesting that exists in a PHP array? An example would be a function that returns 1 if the initial array does not have arrays as elements, 2 if at least one element is an array, and so on.

+2  A: 

I don't think there's anything built in. A simple recursive function could easily find out though.

KernelM
Hmm. It seems like that would be a common thing that would be built in, but I just went over the array functions for the second time and it looks like you are right.
Thomas Owens
+9  A: 

This should do it:

<?php

function array_depth($array) {
    $max_depth = 1;

    foreach ($array as $value) {
        if (is_array($value)) {
            $depth = array_depth($value) + 1;

            if ($depth > $max_depth) {
                $max_depth = $depth;
            }
        }
    }

    return $max_depth;
}

?>

Edit: Tested it very quickly and it appears to work.

yjerem
I'm going to have to test this, but if it works, I like it.
Thomas Owens
+17  A: 

Beware of the examples that just do it recursively.

Php can create arrays with references to other places in that array, and can contain objects with likewise recursive referencing, and any purely recursive algorithm could be considered in such a case a DANGEROUSLY naive one, in that it will overflow stack depth recursing, and never terminate.

( well, it will terminate when it exceeds stack depth, and at that point your program will fatally terminate, not what I think you want )

In past, I have tried serialise -> replacing reference markers with strings -> deserialise for my needs, ( Often debugging backtraces with loads of recursive references in them ) which seems to work OK, you get holes everywhere, but it works for that task.

For your task, if you find your array/structure has recursive references cropping up in it, you may want to take a look at the user contributed comments here: http://nz2.php.net/manual/en/language.references.spot.php

and then somehow find a way to count the depth of a recursive path.

You may need to get out your CS books on algorhthms and hit up these babies:

( Sorry for being so brief, but delving into graph theory is a bit more than suited for this format ;) )

Kent Fredric
+8  A: 

Here's another alternative that avoids the problem Kent Fredric pointed out. It gives print_r() the task of checking for infinite recursion (which it does well) and uses the indentation in the output to find the depth of the array.

function array_depth($array) {
    $max_indentation = 1;

    $array_str = print_r($array, true);
    $lines = explode("\n", $array_str);

    foreach ($lines as $line) {
     $indentation = (strlen($line) - strlen(ltrim($line))) / 4;

     if ($indentation > $max_indentation) {
      $max_indentation = $indentation;
     }
    }

    return ceil(($max_indentation - 1) / 2) + 1;
}
yjerem
that's... actually quite clever.
Nathan Strong
+2  A: 

Here's my slightly modified version of jeremy Ruten's function

// you never know if a future version of PHP will have this in core
if (!function_exists('array_depth')) {
function array_depth($array) {
    // some functions that usually return an array occasionally return false
    if (!is_array($array)) {
        return 0;
    }

    $max_indentation = 1;
    // PHP_EOL in case we're running on Windows
    $lines = explode(PHP_EOL, print_r($array, true));

    foreach ($lines as $line) {
        $indentation = (strlen($line) - strlen(ltrim($line))) / 4;
        $max_indentation = max($max_indentation, $indentation);
    }
    return ceil(($max_indentation - 1) / 2) + 1;
}
}

Things like print array_depth($GLOBALS) won't error due to the recursion, but you may not get the result you expected.

dave1010
+1  A: 
// very simple and clean approach        
function array_depth($a) {
          static $depth = 0;
          if(!is_array($a)) {
            return $depth;
          }else{
            $depth++;
            array_map("array_depth", $a);
            return $depth;
          }
        }
print "depth:" . array_depth(array('k9' => 'dog')); // return 1
Asim
A: 

I had just worked out an answer to this question when I noticed this post. Here was my solution. I haven't tried this on a ton of different array sizes, but it was faster than the 2008 answer for the data I was working with ~30 pieces depth >4.

function deepness(array $arr){
    $exploded = explode(',', json_encode($arr, JSON_FORCE_OBJECT)."\n\n");
    $longest = 0;
    foreach($exploded as $row){
        $longest = (substr_count($row, ':')>$longest)?
            substr_count($row, ':'):$longest;
    }
    return $longest;
}
JoshN
Please note that the options param was not added until php 5.3, so you should convert $arr to an stdClass object if you need to use this answer with 5.2.
JoshN
A: 

I believe the problem highlighted by Kent Frederic is crucial. The answer suggested by yjerem and Asim are vulnerable to this problem.

The approaches by indentation suggested by yjerem again, and dave1010 are not stable enough to me because it relies on the number of spaces that represent an indentation with the print_r function. It might vary with time/server/platform.

The approach suggested by JoshN might be correct, but I think mine is faster :

function array_depth($arr)
{
    if (!is_array($arr)) { return 0; }
    $arr = json_encode($arr);

    $varsum = 0; $depth  = 0;
    for ($i=0;$i<strlen($arr);$i++)
    {
        $varsum += intval($arr[$i] == '[') - intval($arr[$i] == ']');
        if ($varsum > $depth) { $depth = $varsum; }
    }

    return $depth;
}

Post a message if you undertake any testing comparing the different methods. J

Jonathan