Tutorial :Is there a way to find out how “deep” a PHP array is?



Question:

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.


Solution:1

This should do it:

<?php    function array_depth(array $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.


Solution:2

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;  }  


Solution:3

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://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 ;) )


Solution:4

Hi This is an alternative solution.

/*** IN mixed (any value),OUT (string)maxDepth ***/  /*** Retorna la profundidad maxima de un array ***/  function getArrayMaxDepth($input){      if( ! canVarLoop($input) ) { return "0"; }      $arrayiter = new RecursiveArrayIterator($input);      $iteriter = new RecursiveIteratorIterator($arrayiter);      foreach ($iteriter as $value) {              //getDepth() start is 0, I use 0 for not iterable values              $d = $iteriter->getDepth() + 1;              $result[] = "$d";      }      return max( $result );  }  /*** IN mixed (any value),OUT (bool)true/false, CHECK if can be used by foreach ***/  /*** Revisa si puede ser iterado con foreach ***/  function canVarLoop($input) {      return (is_array($input) || $input instanceof Traversable) ? true : false;  }  


Solution:5

After taking a little bit of inspiration here and after finding this RecursiveIteratorIterator thing in PHP Documentation, I came to this solution.

You should use this one, pretty neat :

function getArrayDepth($array) {      $depth = 0;      $iteIte = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));        foreach ($iteIte as $ite) {          $d = $iteIte->getDepth();          $depth = $d > $depth ? $d : $depth;      }        return $depth;  }  

Works on both PHP5 and PHP7, hope this helps.


Solution:6

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;  }  

Warning: this doesn't handle any edge cases. If you need a robust solution look elsewhere, but for the simple case I found this to be pretty fast.


Solution:7

Another (better) modification to the function from Jeremy Ruten:

function array_depth($array, $childrenkey = "_no_children_")  {      if (!empty($array[$childrenkey]))      {          $array = $array[$childrenkey];      }        $max_depth = 1;        foreach ($array as $value)      {          if (is_array($value))          {              $depth = array_depth($value, $childrenkey) + 1;                if ($depth > $max_depth)              {                  $max_depth = $depth;              }          }      }        return $max_depth;  }  

Adding a default value to $childrenkey allows the function to work for simple array with no keys for child elements, i.e. it will work for simple multi-dimensional arrays.

This function can now be called using:

$my_array_depth = array_depth($my_array, 'the_key_name_storing_child_elements');  

or

$my_array_depth = array_depth($my_array);  

when $my_array doesn't have any specific key for storing its child elements.


Solution:8

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.


Solution:9

function createDeepArray(){      static $depth;      $depth++;      $a = array();      if($depth <= 10000){          $a[] = createDeepArray();      }      return $a;  }  $deepArray = createDeepArray();    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;  }    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;  }    echo 'deepness():', "\n";    $start_time = microtime(TRUE);  $start_memory = memory_get_usage();  var_dump(deepness($deepArray));  $end_time = microtime(TRUE);  $end_memory = memory_get_usage();  echo 'Memory: ', ($end_memory - $start_memory), "\n";  echo 'Time: ', ($end_time - $start_time), "\n";    echo "\n";  echo 'array_depth():', "\n";    $start_time = microtime(TRUE);  $start_memory = memory_get_usage();  var_dump(array_depth($deepArray));  $end_time = microtime(TRUE);  $end_memory = memory_get_usage();  echo 'Memory: ', ($end_memory - $start_memory), "\n";  echo 'Time: ', ($end_time - $start_time), "\n";  

The function proposed by Josh was definitely faster:

$ for i in `seq 1 10`; do php test.php; echo '-------------------------';done  deepness():  int(10000)  Memory: 164  Time: 0.0079939365386963    array_depth():  int(10001)  Memory: 0  Time: 0.043087005615234  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0076408386230469    array_depth():  int(10001)  Memory: 0  Time: 0.042832851409912  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0080249309539795    array_depth():  int(10001)  Memory: 0  Time: 0.042320966720581  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0076301097869873    array_depth():  int(10001)  Memory: 0  Time: 0.041887998580933  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0079131126403809    array_depth():  int(10001)  Memory: 0  Time: 0.04217004776001  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0078539848327637    array_depth():  int(10001)  Memory: 0  Time: 0.04179310798645  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0080208778381348    array_depth():  int(10001)  Memory: 0  Time: 0.04272198677063  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0077919960021973    array_depth():  int(10001)  Memory: 0  Time: 0.041619062423706  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0080950260162354    array_depth():  int(10001)  Memory: 0  Time: 0.042663097381592  -------------------------  deepness():  int(10000)  Memory: 164  Time: 0.0076849460601807    array_depth():  int(10001)  Memory: 0  Time: 0.042278051376343  


Solution:10

An old question, yet remain relevant to this date. :)

Might as well contribute a minor modification to the answer from Jeremy Ruten.

function array_depth($array, $childrenkey)  {      $max_depth = 1;        if (!empty($array[$childrenkey]))      {          foreach ($array[$childrenkey] as $value)          {              if (is_array($value))              {                  $depth = array_depth($value, $childrenkey) + 1;                    if ($depth > $max_depth)                  {                      $max_depth = $depth;                  }              }          }      }        return $max_depth;  }  

I added a second parameter called $childrenkey because I store the child elements in a specific key.

An example of the function call is:

$my_array_depth = array_depth($my_array, 'the_key_name_storing_child_elements');  


Solution:11

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


Solution:12

// 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  


Solution:13

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


Solution:14

I believe you forgot to filter '[' and ']' or ',' and ':' and the data type of the array's key(s) and value(s). Here's an update of your array_depth plus a bonus array_sort_by_depth.

function array_depth($arr){  if (is_array($arr)) {      array_walk($arr,           function($val, $key) use(&$arr) {              if ((! is_string($val)) && (! is_array($val))) {                  $val = json_encode($val, JSON_FORCE_OBJECT);              }                if (is_string($val)) {                  $arr[$key] = preg_replace('/[:,]+/', '', $val);              }          }      );        $json_strings = explode(',', json_encode($arr, JSON_FORCE_OBJECT));        $max_depth = 0;        foreach ($json_strings as $json_string){          var_dump($json_string); echo "<br/>";          $json_string = preg_replace('/[^:]{1}/', '', $json_string);          var_dump($json_string); echo "<br/><br/>";          $depth = strlen($json_string);            if ($depth > $max_depth) {              $max_depth = $depth;          }      }                return $max_depth;      }        return FALSE;      }          function array_sort_by_depth(&$arr_val, $reverse = FALSE) {      if ( is_array($arr_val)) {       $temp_arr = array();              $result_arr = array();                foreach ($arr_val as $key => $val) {                  $temp_arr[$key] = array_depth($val);              }            if (is_bool($reverse) && $reverse == TRUE) {                  arsort($temp_arr);              }              else {                  asort($temp_arr);              }                foreach ($temp_arr as $key => $val) {                  $result_arr[$key] = $arr_val[$key];              }                $arr_val = $result_arr;        return TRUE;       }         return FALSE;    }  

Feel free to improve the code :D!


Solution:15

I think this would solve the recursion problem, and also give the depth without relying on other php functions like serialize or print_r (which is risky at best and can lead to intractable bugs):

function array_depth(&$array) {      $max_depth = 1;      $array['__compute_array_depth_flag_ZXCNADJHHDKAQP'] = 1;        foreach ($array as $value) {          if (is_array($value) &&                      !isset($value['__compute_array_depth_flag_ZXCNADJHHDKAQP']))  {              $depth = array_depth($value) + 1;                if ($depth > $max_depth) {                  $max_depth = $depth;              }          }      }      unset($array['__compute_array_depth_flag_ZXCNADJHHDKAQP']);        return $max_depth;  }  


Solution:16

This one seems to work fine for me

<?php  function array_depth(array $array)  {      $depth = 1;      foreach ($array as $value) {          if (is_array($value)) {              $depth += array_depth($value);              break;          }      }        return $depth;  }  


Solution:17

We can make json encode of array and then count max count of array open braces at same time.

function max_depth($arr){        // json encode      $string = json_encode($arr);      // removing string values to avoid braces in strings      $string = preg_replace('/\"(.*?)\"/', '""', $string);      //Replacing object braces with array braces      $string = str_replace(['{', '}'], ['[', ']'], $string);        $length = strlen($string);      $now = $max = 0;        for($i = 0; $i < $length; $i++){          if($string[$i] == '['){              $now++;              $max = $max < $now ? $now : $max          }            if($string[$i] == ']'){              $now--;          }      }        return $max;  }  

Note: this will not work if you have objects in your array.


Solution:18

I would use the following code:

function maxDepth($array) {      $iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array), \RecursiveIteratorIterator::CHILD_FIRST);      $iterator->rewind();      $maxDepth = 0;      foreach ($iterator as $k => $v) {          $depth = $iterator->getDepth();          if ($depth > $maxDepth) {              $maxDepth = $depth;          }      }      return $maxDepth;  }  


Solution:19

A quicker way:

max(array_map('count', $array));  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »