В массиве PHP могут быть массивы для своих элементов. И эти массивы могут иметь массивы и т.д. И т.д. Есть ли способ узнать максимальное гнездование, существующее в массиве PHP? Примером может служить функция, которая возвращает 1, если исходный массив не имеет массивов в качестве элементов, 2, если хотя бы один элемент является массивом и т.д.
Есть ли способ узнать, как "глубокий" массив PHP?
Ответ 1
Это должно сделать это:
<?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;
}
?>
Изменить: протестировано очень быстро и, похоже, работает.
Ответ 2
Вот еще одна альтернатива, которая позволяет избежать проблемы, о которой указал Кент Фредрик. Он дает 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;
}
Ответ 3
Остерегайтесь примеров, которые просто делают это рекурсивно.
Php может создавать массивы со ссылками на другие места в этом массиве и может содержать объекты с аналогичной рекурсивной ссылкой, и любой чисто рекурсивный алгоритм можно рассматривать в таком случае ОПАСНО наивно, поскольку он будет переполнять глубину стека, и никогда не заканчиваются.
(ну, он будет прерван, когда он превысит глубину стека, и в этот момент ваша программа будет смертельно закончиться, а не то, что я думаю, что вы хотите)
В прошлом я пробовал сериализацию → заменяя ссылочные маркеры на строки → десериализацию для моих нужд (часто отлаживая обратные трассировки с множеством рекурсивных ссылок в них), которые, похоже, работают нормально, вы получаете дыры везде, но это работает для этой задачи.
Для вашей задачи, если вы обнаружите, что ваш массив/структура имеет рекурсивные ссылки, возникающие в нем, вы можете взглянуть на комментарии, внесенные пользователем: http://php.net/manual/en/language.references.spot.php
а затем каким-то образом найти способ подсчета глубины рекурсивного пути.
Возможно, вам нужно будет вытащить свои книги CS по алгоритмам и поразить этих детей:
(Извините за то, что так краток, но вникание в теорию графов немного больше подходит для этого формата;))
Ответ 4
Привет Это альтернативное решение.
/*** 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;
}
Ответ 5
Я только что разработал ответ на этот вопрос, когда заметил этот пост. Вот мое решение. Я не пробовал это на тоннах разных размеров массива, но он был быстрее, чем ответ 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;
}
Предупреждение: это не обрабатывает случаи ребер. Если вам нужно надежное решение искать в другом месте, но для простого случая я нашел, что это довольно быстро.
Ответ 6
Другая (лучшая) модификация функции от Джереми Рутена:
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 не имеет специального ключа для хранения дочерних элементов.
Ответ 7
После немного вдохновения здесь и после обнаружения этой РекурсивныйИмятератор в документации 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, надеюсь, что это поможет.
Ответ 8
Здесь моя слегка измененная версия функции 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)
, не будут ошибки из-за рекурсии, но вы не можете получить ожидаемый результат.
Ответ 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";
Функция, предложенная Джошем, была определенно быстрее:
$ 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
Старый вопрос, но остается актуальным для этой даты.:)
Возможно также внести незначительные изменения в ответ от Джереми Рутена.
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');
Ответ 11
Я не думаю, что там что-то встроено. Простая рекурсивная функция может быть легко обнаружена.
Ответ 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
Ответ 13
Я считаю, что проблема, отмеченная Кентом Фредериком, имеет решающее значение. Ответ, предложенный 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
Ответ 14
Я считаю, что вы забыли фильтровать '[' и ']' или ',' и ':' и тип данных ключей (ов) массива и значений. Здесь обновляется массив 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!
Ответ 15
Я думаю, что это решит проблему рекурсии, а также даст глубину, не полагаясь на другие функции 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;
}
Ответ 16
Этот, кажется, работает отлично для меня
<?php
function array_depth(array $array)
{
$depth = 1;
foreach ($array as $value) {
if (is_array($value)) {
$depth += array_depth($value);
break;
}
}
return $depth;
}
Ответ 17
Более быстрый способ:
max(array_map('count', $array));
Ответ 18
Мы можем сделать json-кодирование массива, а затем подсчитать максимальное количество открытых скобок массива одновременно.
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;
}
Примечание: это не сработает, если в вашем массиве есть объекты.