web-dev-qa-db-fra.com

Existe-t-il un moyen de déterminer la profondeur d'un tableau PHP)?

Un PHP tableau peut avoir des tableaux pour ses éléments. Et ces tableaux peuvent avoir des tableaux et ainsi de suite et ainsi de suite. Existe-t-il un moyen de découvrir l'imbrication maximale qui existe dans un PHP array? Un exemple serait une fonction qui renvoie 1 si le tableau initial n'a pas de tableaux en tant qu'éléments, 2 si au moins un élément est un tableau, et ainsi de suite.

49
Thomas Owens

Cela devrait le faire:

<?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: Testé très rapidement et il semble fonctionner.

61
Jeremy Ruten

Voici une autre alternative qui évite le problème signalé par Kent Fredric. Il donne à print_r () la tâche de vérifier la récursion infinie (ce qu'il fait bien) et utilise l'indentation dans la sortie pour trouver la profondeur du tableau.

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;
}
73
Jeremy Ruten

Méfiez-vous des exemples qui le font récursivement.

Php peut créer des tableaux avec des références à d'autres endroits de ce tableau, et peut contenir des objets avec un référencement également récursif, et tout algorithme purement récursif pourrait être considéré dans un tel cas a [~ # ~] dangereusement [~ # ~] naïf, car il débordera la profondeur de pile de manière récurrente, et ne se terminera jamais.

(eh bien, il se terminera lorsqu'il dépassera la profondeur de la pile, et à ce moment-là, votre programme se terminera fatalement, pas ce que je pense que vous voulez)

Dans le passé, j'ai essayé de sérialiser -> remplacer les marqueurs de référence par des chaînes -> désérialiser pour mes besoins, (débogage souvent des tracés avec de nombreuses références récursives) qui semble fonctionner correctement, vous obtenez des trous partout, mais cela fonctionne pour cette tâche .

Pour votre tâche, si vous trouvez que votre tableau/structure contient des références récursives, vous pouvez consulter les commentaires des utilisateurs ici: http://php.net/manual/en/language .references.spot.php

puis trouver en quelque sorte un moyen de compter la profondeur d'un chemin récursif.

Vous devrez peut-être sortir vos livres CS sur les algorithmes et frapper ces bébés:

(Désolé d'être si bref, mais plonger dans la théorie des graphes est un peu plus que adapté à ce format;))

43
Kent Fredric

Salut C'est une solution alternative.

/*** 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;
}
9
Erick Briseño

Après avoir pris un peu d'inspiration ici et après avoir trouvé cela RecursiveIteratorIterator chose dans = PHP Documentation, je suis arrivé à cette solution.

Vous devriez utiliser celui-ci, plutôt soigné:

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

Fonctionne à la fois sur PHP5 et PHP7, j'espère que cela vous aidera.

7
TwystO

Je venais de trouver une réponse à cette question lorsque j'ai remarqué ce post. Voici ma solution. Je n'ai pas essayé cela sur une tonne de tailles de tableaux différentes, mais c'était plus rapide que la réponse de 2008 pour les données avec lesquelles je travaillais avec ~ 30 pièces de profondeur> 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;
}

Attention: cela ne gère aucun cas Edge. Si vous avez besoin d'une solution robuste, regardez ailleurs, mais pour le cas simple, j'ai trouvé que c'était assez rapide.

4
fncomp

Une autre (meilleure) modification de la fonction 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;
}

L'ajout d'une valeur par défaut à $ childrenkey permet à la fonction de fonctionner pour un tableau simple sans clé pour les éléments enfants, c'est-à-dire qu'il travailler pour des tableaux multidimensionnels simples.

Cette fonction peut maintenant être appelée en utilisant:

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

ou

$my_array_depth = array_depth($my_array);

quand $ my_array n'a pas de clé spécifique pour stocker ses éléments enfants.

3
Amir Syafrudin

Voici ma version légèrement modifiée de la fonction 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;
}
}

Des choses comme print array_depth($GLOBALS) n'erreront pas en raison de la récursivité, mais vous n'obtiendrez peut-être pas le résultat escompté.

3
dave1010

Une vieille question, mais qui reste d'actualité pour cette date. :)

Pourrait aussi bien apporter une modification mineure à la réponse 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;
}

J'ai ajouté un deuxième paramètre appelé $ childrenkey parce que je stocke les éléments enfants dans une clé spécifique.

Un exemple de l'appel de fonction est:

$my_array_depth = array_depth($my_array, 'the_key_name_storing_child_elements');
2
Amir Syafrudin
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 fonction proposée par Josh était nettement plus rapide:

$ 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
shachibista

Je crois que le problème mis en évidence par Kent Frederic est crucial. La réponse suggérée par yjerem et Asim est vulnérable à ce problème.

Les approches par indentation suggérées à nouveau par yjerem et dave1010 ne sont pas assez stables pour moi car elles reposent sur le nombre d'espaces qui représentent une indentation avec la fonction print_r. Cela peut varier selon l'heure/le serveur/la plate-forme.

L'approche suggérée par JoshN pourrait être correcte, mais je pense que la mienne est plus rapide:

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

Postez un message si vous effectuez des tests comparant les différentes méthodes. J

1
Sheljohn
// 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
1
Asim

Celui-ci semble bien fonctionner pour moi

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

    return $depth;
}
1
Linguisto

Je pense que cela résoudrait le problème de récursivité, et donnerait également de la profondeur sans compter sur d'autres fonctions php comme serialize ou print_r (ce qui est au mieux risqué et peut conduire à des bugs insolubles):

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

Je crois que vous avez oublié de filtrer "[" et "]" ou "," et ":" et le type de données des clés et des valeurs du tableau. Voici une mise à jour de votre array_depth plus 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;
  }

N'hésitez pas à améliorer le code: D!

1
cmosversion

Je ne pense pas qu'il y ait quoi que ce soit intégré. Une simple fonction récursive pourrait facilement le découvrir.

1
KernelM

Nous pouvons faire le codage json du tableau puis compter le nombre maximal d'accolades ouvertes du tableau en même temps.

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

Remarque: cela ne fonctionnera pas si vous avez des objets dans votre tableau.

0
Narek

J'utiliserais le code suivant:

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