有没有办法找出 PHP 数组的“深度”?

Posted

技术标签:

【中文标题】有没有办法找出 PHP 数组的“深度”?【英文标题】:Is there a way to find out how "deep" a PHP array is? 【发布时间】:2010-09-20 17:52:32 【问题描述】:

一个 php 数组可以有其元素的数组。这些数组可以有数组等等。有没有办法找出 PHP 数组中存在的最大嵌套?一个例子是一个函数,如果初始数组没有数组作为元素,则返回 1,如果至少一个元素是数组,则返回 2,依此类推。

【问题讨论】:

【参考方案1】:

我认为没有内置任何东西。不过,一个简单的递归函数很容易找到。

【讨论】:

嗯。看起来这将是一个内置的常见东西,但我只是第二次检查了数组函数,看起来你是对的。【参考方案2】:

应该这样做:

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


?>

编辑:非常快速地对其进行了测试,并且似乎可以正常工作。

【讨论】:

【参考方案3】:

谨防递归执行的示例。

Php 可以创建具有对该数组中其他位置的引用的数组,并且可以包含具有同样递归引用的对象,并且在这种情况下,任何纯递归算法都可以被认为是一种危险天真的算法,在它将溢出堆栈深度递归,并且永远不会终止。

(好吧,它会在超过堆栈深度时终止,此时您的程序将致命地终止,而不是我认为您想要的)

过去,我尝试过序列化 -> 用字符串替换引用标记 -> 反序列化以满足我的需要,(通常调试带有大量递归引用的回溯),这似乎工作正常,你到处都有漏洞,但它工作完成这项任务。

对于您的任务,如果您发现您的数组/结构中出现了递归引用,您可能需要在此处查看用户贡献的 cmets:http://php.net/manual/en/language.references.spot.php

然后以某种方式找到一种方法来计算递归路径的深度。

你可能需要拿出你关于算法的 CS 书籍,然后去找这些宝贝:

Wiki:Depth-limited-search Wiki:Depth-first search

(抱歉这么简短,但钻研图论有点不适合这种格式;))

【讨论】:

【参考方案4】:

这是避免 Kent Fredric 指出的问题的另一种选择。它为print_r() 提供了检查无限递归的任务(它做得很好),并使用输出中的缩进来查找数组的深度。

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;

【讨论】:

这……其实很聪明。 这个函数应该返回int而不是float(来自ceil)。【参考方案5】:

这是我对 jeremy Ruten 函数稍作修改的版本

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


print array_depth($GLOBALS) 这样的东西不会因为递归而出错,但你可能不会得到你期望的结果。

【讨论】:

【参考方案6】:
// 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

【讨论】:

@user699082 - 如果您对这篇文章有任何疑问,请不要在整个答案中乱写您的投诉。请改用 cmets。 深 X 失败,在这种情况下,深 = 2 -> $Q = ARRAY(1, 2, 3, ARRAY(), ARRAY(), ARRAY()); echo array_depth($Q);【参考方案7】:

当我注意到这篇文章时,我刚刚找到了这个问题的答案。这是我的解决方案。我还没有在大量不同的数组大小上尝试过这个,但它比 2008 年我使用的数据的答案要快,大约 30 件深度 >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;

警告:这不处理任何个边缘情况。如果您需要一个强大的解决方案,请查看其他地方,但对于简单的情况,我发现这非常快。

【讨论】:

请注意,选项参数直到 php 5.3 才添加,因此如果您需要在 5.2 中使用此答案,则应将 $arr 转换为 stdClass 对象。 这种方法有一个很大的缺陷。数组文本中有任何冒号都会返回误报。因此 ['x'=>'a:b:c'] 将返回深度 4。 好点,我提出了警告。我在 4 年前写了这篇文章,完全忘记了它。 FWIW 在 PHP 4.x 下它非常快我不知道这是否仍然很理智。【参考方案8】:

我认为 Kent Frederic 强调的问题至关重要。 yjerem 和 Asim 提出的答案很容易受到这个问题的影响。

yjerem 再次建议的缩进方法和 dave1010 对我来说不够稳定,因为它依赖于代表 print_r 函数缩进的空格数。它可能因时间/服务器/平台而异。

JoshN 建议的方法可能是正确的,但我认为我的方法更快:

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;

如果您进行任何比较不同方法的测试,请发布消息。 J

【讨论】:

感谢@Jonathan H,您的代码对我来说运行良好,只是......¿真的是“确认/安全”吗?我的问题是因为这个解决方案依赖于sintax of function json_encode() 并且......也许是危险的依赖于 sintaxis?【参考方案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";

Josh 提出的功能肯定更快:

$ 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

【讨论】:

【参考方案10】:

我相信您忘记过滤 '[' 和 ']' 或 ',' 和 ':' 以及数组键和值的数据类型。这是您的 array_depth 的更新以及额外的 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;
  

请随意改进代码:D!

【讨论】:

【参考方案11】:

我认为这将解决递归问题,并且在不依赖其他 php 函数(如 serialize 或 print_r )的情况下提供深度(这充其量是有风险的,并且可能导致棘手的错误):

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;

【讨论】:

【参考方案12】:

更快的方法:

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

【讨论】:

【参考方案13】:

一个老问题,但与这个日期仍然相关。 :)

不妨对 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;

我添加了第二个名为 $childrenkey 的参数,因为我将子元素存储在特定键中。

函数调用的一个例子是:

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

【讨论】:

【参考方案14】:

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;

$childrenkey 添加默认值 允许该函数适用于没有子元素键的简单数组,即它适用于简单的多维数组。

现在可以使用以下方法调用此函数:

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

$my_array_depth = array_depth($my_array);

$my_array 没有任何特定的键来存储其子元素时。

【讨论】:

对变量使用“&”以避免复制它们。我认为它会提高你的代码的性能。例如“foreach ($array as &$value)” 感谢您的建议。只是将参数从按值传递更改为按引用传递(使用“&”)可能需要对代码进行整体修改。例如,$max_depth$depth 变量也应该在使用“&”的递归调用期间传递,并且应该丢弃“return”语句。该代码与 Jeremy Ruten 建议的代码大不相同。 :) 并非如此,您需要做的只是插入两个 & 符号。一个在'foreach'语句中(正如我之前提到的)和另一个在函数参数'array_depth(&$array,'之前。我相信没有任何变化就足够了。 提到的两个和号将阻止 PHP 在每次传递/迭代时复制数组。当您在 foreach 中以 foreach ($arr as $key =&gt; $value) 迭代数组时,提取的每个 $value 不是原始数组中的相同元素,而是它的副本。但是当您编写foreach ($arr as $key =&gt; &amp;$value) 时,$value 将是数组中的确切元素,更改它将导致更改原始数组。在您的情况下,它将阻止 PHP 复制每个数组的元素,从而提高性能。 只是对使用引用的评论。我可能是错的,但我记得几年前在某本书中读到,php 在对其进行更改之前不会复制变量。因此,不应该制作大量的副本,但它仍然会充当参考,直到其中一个变量对其进行了更改。由于没有修改任何键并且只读取,因此不会导致任何性能问题。正如我所说,我可能是错的,但如果有人知道这是否正确,你能验证一下吗?如果我找到关于此的帖子,我会确认这是否属实。【参考方案15】:

您好,这是另一种解决方案。

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

【讨论】:

简单,因为它不使用任何黑客并让 PHP 处理它:php.net/RecursiveIteratorIterator.getDepth【参考方案16】:

这个对我来说似乎很好用

<?php
function array_depth(array $array)

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

    return $depth;

【讨论】:

当 array 的后面的孩子之一是比它的先前兄弟之一更深的数组时,这不会很好。【参考方案17】:

在这里获得一点灵感并在 PHP 文档中找到这个 RecursiveIteratorIterator 之后,我来到了这个解决方案。

你应该使用这个,非常整洁:

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;

适用于 PHP5 和 PHP7,希望对您有所帮助。

【讨论】:

不幸的是,测试这个函数返回少一个,所以对于深度为 5 的数组,它返回 4 从 0 开始计数是编程中的正常行为。如果它触发了您,只需在结果中添加 +1 或将 $depth 更改为等于 1。 此外,此功能不适用于空数组或有键但没有值的数组。 “不起作用”是什么意思?请让我知道您遇到的问题。在这里工作得很好:sandbox.onlinephpfunctions.com/code/… sandbox.onlinephpfunctions.com/code/…【参考方案18】:

我会使用以下代码:

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;

【讨论】:

【参考方案19】:
//Get the dimension or depth of an array
function array_depth($arr)

    if (!is_array($arr)) return 0;
    if (empty($arr))     return 1;
    return max(array_map(__FUNCTION__,$arr))+1;

【讨论】:

因深 X 和值为空而失败:$Q = ARRAY(1, 2, 3, ARRAY()); echo array_depth($Q); 结果是 2,没关系。 @Paige Ruten 接受的解决方案也提供了这一点。【参考方案20】:

在我的解决方案中,我评估 ARRAY() 的维度,而不是内容/值:

function Dim_Ar($A, $i)
    if(!is_array($A))return 0;
    $t[] = 1;
    foreach($A AS $e)if(is_array($e))$t[] = Dim_Ar($e, ++ $i) + 1;
    return max($t);
    

$Q = ARRAY();                                                                               // dimension one
$Q = ARRAY(1);                                                                          // dimension one
$Q = ARRAY(ARRAY(ARRAY()), ARRAY(1, 1, 1));                 // dimension is two
$Q = ARRAY(ARRAY());                                                                // dimension is two
$Q = ARRAY(1, 1, 1, ARRAY(), ARRAY(), ARRAY(1));        // dimension is two
$Q = ARRAY(1, 2, 3, ARRAY(ARRAY(1, 1, 1)));                 // dimension is two
$Q = ARRAY(ARRAY(ARRAY()), ARRAY());                                // dimension is three
$Q = ARRAY(ARRAY(ARRAY()), ARRAY());                                // dimension three
$Q = ARRAY(ARRAY(ARRAY()), ARRAY(ARRAY()));                 // dimension is three
$Q = ARRAY('1', '2', '3', ARRAY('Q', 'W'), ARRAY('Q', 'W'), ARRAY('Q', 'W'), ARRAY('Q', 'W'), 'pol, y juan', 'sam, y som', '1', '2', 'OPTIONS1' => ARRAY('1', '2', '9'), 'OOO' => ARRAY('1', '2', '9'), 'OPTIONS3' => ARRAY('1', '2', '9', '1', '2', '9', '1', '2', '9', '1', '2', '9', '1', '2', '9'), '3', ARRAY('Q', 'W'), 'OPTIONS2' => ARRAY('1', '2'));
$Q = ARRAY('1', '2', '3', '', ARRAY('Q, sam', 'W', '', '0'), 'ppppppol, y juan', 'sam, y som', '1', '2', 'OPTIONS1' => ARRAY('1', '2', 'ss, zz'), '3', 'PP' => ARRAY('Q', 'WWW', 'Q', 'BMW'), ARRAY('Q', 'YYWW'), 'OPTIONS2' => ARRAY('1', '2', '9'), ARRAY('1', '2', '3'), '33', '33', '33', ARRAY('1', '2', '3', ARRAY(1, 2)));

echo Dim_Ar($Q, 0);

对我来说是速度和低复杂度

【讨论】:

以上是关于有没有办法找出 PHP 数组的“深度”?的主要内容,如果未能解决你的问题,请参考以下文章

PHP中目录结构的深度递归数组

深度解析PHP数组函数array_chunk

PHP 的替代 var_dump 允许限制嵌套数组的深度

Leetcode练习(Python):树类:第104题:二叉树的最大深度:给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点

深度解析PHP数组函数array_slice

深度解析PHP数组函数array_combine