Есть ли способ узнать, насколько «глубокий» массив PHP? - PullRequest
49 голосов
/ 04 ноября 2008

Массив PHP может иметь массивы для своих элементов. И эти массивы могут иметь массивы и так далее и тому подобное. Есть ли способ узнать максимальное вложение, которое существует в массиве PHP? Примером может служить функция, которая возвращает 1, если исходный массив не имеет массивов в качестве элементов, 2, если хотя бы один элемент является массивом, и т. Д.

Ответы [ 19 ]

73 голосов
/ 05 ноября 2008

Вот еще одна альтернатива, которая позволяет избежать проблемы, на которую указал Кент Фредрик. Это дает 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;
}
60 голосов
/ 04 ноября 2008

Это должно сделать это:

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

?>

Редактировать: очень быстро протестировано, и похоже, что оно работает.

43 голосов
/ 04 ноября 2008

Остерегайтесь примеров, которые просто делают это рекурсивно.

Php может создавать массивы со ссылками на другие места в этом массиве и может содержать объекты с аналогичной рекурсивной ссылкой, и любой чисто рекурсивный алгоритм может быть рассмотрен в таком случае как ОПАСНО наивный, в котором он переполняет глубину стека и никогда не завершается.

(что ж, он прекратит работу, когда он превысит глубину стека, и в этот момент ваша программа будет фатально прервана, а не то, что я думаю, вы хотите)

В прошлом я пытался сериализовать -> заменять маркеры ссылок на строки -> десериализовать для своих нужд (часто отладка обратных трасс с множеством рекурсивных ссылок в них), что, кажется, работает нормально, вы везде получаете дыры, но это работает для этой задачи.

Для вашей задачи, если вы обнаружите, что в вашем массиве / структуре есть рекурсивные ссылки, вы, возможно, захотите взглянуть на комментарии, добавленные пользователем здесь: http://php.net/manual/en/language.references.spot.php

и затем каким-то образом найти способ подсчитать глубину рекурсивного пути.

Возможно, вам понадобится вынуть ваши книги CS по алгоритмам и поразить этих детей:

(Извините за краткость, но углубление в теорию графов немного больше, чем подходит для этого формата;))

9 голосов
/ 26 сентября 2013

Привет Это альтернативное решение.

/*** 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;
}
7 голосов
/ 21 ноября 2016

После небольшого вдохновения здесь и после того, как я нашел RecursiveIteratorIterator в документации PHP, я пришел к этому решению.

Вы должны использовать это, довольно аккуратно:

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, надеюсь, это поможет.

4 голосов
/ 10 октября 2010

Я только что разработал ответ на этот вопрос, когда заметил этот пост. Здесь было мое решение. Я не пробовал это на тоннах разных размеров, но это было быстрее, чем ответ 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;
}

Предупреждение : это не обрабатывает любые крайние случаи. Если вам нужно надежное решение, посмотрите в другом месте, но для простого случая я нашел это довольно быстрым.

3 голосов
/ 28 октября 2012

Еще одна (лучшая) модификация функции от Джереми Рутена:

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 не имеет какого-либо специального ключа для хранения своих дочерних элементов.

2 голосов
/ 28 октября 2012

Старый вопрос, но остается актуальным для этой даты. :)

Может также внести небольшую модификацию в ответ от Джереми Рутена.

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');
2 голосов
/ 20 февраля 2011

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

Функция, предложенная Джошем, была определенно быстрее:

$ 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
2 голосов
/ 13 апреля 2010

Вот моя слегка измененная версия функции Джереми Рутена

// 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) не будут ошибаться из-за рекурсии, но вы можете не получить ожидаемый результат.

...