¿Hay alguna manera de averiguar cómo & # 8220; profundo & # 8221; es una matriz PHP?

StackOverflow https://stackoverflow.com/questions/262891

  •  06-07-2019
  •  | 
  •  

Pregunta

Una matriz PHP puede tener matrices para sus elementos. Y esas matrices pueden tener matrices y así sucesivamente. ¿Hay alguna manera de averiguar la anidación máxima que existe en una matriz PHP? Un ejemplo sería una función que devuelve 1 si la matriz inicial no tiene matrices como elementos, 2 si al menos un elemento es una matriz, y así sucesivamente.

¿Fue útil?

Solución

Esto debería hacerlo:

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

?>

Editar: lo probé muy rápidamente y parece funcionar.

Otros consejos

Aquí hay otra alternativa que evita el problema que señaló Kent Fredric. Le da a print_r () la tarea de verificar la recursividad infinita (que funciona bien) y utiliza la sangría en la salida para encontrar la profundidad de la matriz.

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

Cuidado con los ejemplos que solo lo hacen de forma recursiva.

Php puede crear matrices con referencias a otros lugares en esa matriz, y puede contener objetos con referencias recursivas similares, y cualquier algoritmo puramente recursivo podría considerarse en tal caso un PELIGROSAMENTE ingenuo, en que desbordará la profundidad de la pila recurriendo, y nunca terminará.

(bueno, terminará cuando exceda la profundidad de la pila, y en ese punto su programa terminará fatalmente, no es lo que creo que quiere)

En el pasado, he intentado serializar - > reemplazo de marcadores de referencia con cadenas - > deserializar para mis necesidades (a menudo, depurar trazas con un montón de referencias recursivas en ellas) que parece funcionar bien, tienes agujeros en todas partes, pero funciona para esa tarea.

Para su tarea, si encuentra que su matriz / estructura tiene referencias recursivas que surgen en ella, puede echar un vistazo a los comentarios aportados por el usuario aquí: http://php.net/manual/en/language.references.spot.php

y luego de alguna manera encuentra una manera de contar la profundidad de un camino recursivo.

Es posible que necesite sacar sus libros de CS sobre algoritmos y golpear a estos bebés:

(Perdón por ser tan breve, pero profundizar en la teoría de gráficos es un poco más que adecuado para este formato;))

Hola, esta es una solución alternativa.

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

Después de tomar un poco de inspiración aquí y después de encontrar esto RecursiveIteratorIterator en la documentación de PHP, llegué a esta solución.

Deberías usar este, bastante ordenado:

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

Funciona en PHP5 y PHP7, espero que esto ayude.

Acababa de encontrar una respuesta a esta pregunta cuando noté esta publicación. Aquí estaba mi solución. No he probado esto en una tonelada de diferentes tamaños de matriz, pero fue más rápido que la respuesta de 2008 para los datos con los que estaba trabajando con ~ 30 piezas de profundidad > 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;
}

Advertencia : esto no maneja ningún caso límite. Si necesita una solución sólida, busque en otro lado, pero por el simple caso, encontré que esto es bastante rápido.

Otra (mejor) modificación a la función de 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;
}

Agregar un valor predeterminado a $ childrenkey permite que la función funcione para una matriz simple sin claves para elementos secundarios, es decir, funcionará para matrices multidimensionales simples.

Esta función ahora se puede llamar usando:

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

o

$my_array_depth = array_depth($my_array);

cuando $ my_array no tiene ninguna clave específica para almacenar sus elementos secundarios.

Aquí está mi versión ligeramente modificada de la función de 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;
}
}

Cosas como print array_depth ($ GLOBALS) no se producirán errores debido a la recurrencia, pero es posible que no obtenga el resultado que esperaba.

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

La función propuesta por Josh fue definitivamente más rápida:

$ 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

Una pregunta anterior, pero sigue siendo relevante para esta fecha. :)

También podría contribuir una modificación menor a la respuesta de 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;
}

Agregué un segundo parámetro llamado $ childrenkey porque almaceno los elementos secundarios en una clave específica.

Un ejemplo de la llamada a la función es:

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

No creo que haya nada incorporado. Sin embargo, una función recursiva simple podría descubrirse fácilmente.

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

Creo que el problema destacado por Kent Frederic es crucial. La respuesta sugerida por yjerem y Asim es vulnerable a este problema.

Los enfoques por sangría sugeridos por yjerem nuevamente, y dave1010 no son lo suficientemente estables para mí porque depende del número de espacios que representan una sangría con la función print_r. Puede variar con el tiempo / servidor / plataforma.

El enfoque sugerido por JoshN podría ser correcto, pero creo que el mío es más rápido:

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

Publique un mensaje si realiza alguna prueba comparando los diferentes métodos. J

Creo que olvidó filtrar '[' y ']' o ',' y ':' y el tipo de datos de la (s) clave (s) y valor (es) de la matriz. Aquí hay una actualización de su array_depth más un 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;
  }

Siéntase libre de mejorar el código: D!

Creo que esto resolvería el problema de recursión y también daría profundidad sin depender de otras funciones de php como serialize o print_r (que en el mejor de los casos es arriesgado y puede provocar errores intratables):

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

Este parece funcionar bien para mí

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

    return $depth;
}

Usaría el siguiente código:

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

Una forma más rápida:

max(array_map('count', $array));
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top