Domanda

Un array PHP può avere array per i suoi elementi. E quelle matrici possono avere matrici e così via e così via. Esiste un modo per scoprire l'annidamento massimo esistente in un array PHP? Un esempio potrebbe essere una funzione che restituisce 1 se l'array iniziale non ha array come elementi, 2 se almeno un elemento è un array e così via.

È stato utile?

Soluzione

Questo dovrebbe farlo:

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

?>

Modifica: testato molto rapidamente e sembra funzionare.

Altri suggerimenti

Ecco un'altra alternativa che evita il problema sottolineato da Kent Fredric. Assegna a print_r () il compito di verificare la ricorsione infinita (cosa che fa bene) e usa il rientro nell'output per trovare la profondità dell'array.

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

Attenzione degli esempi che lo fanno in modo ricorsivo.

Php può creare array con riferimenti ad altri posti in quell'array e può contenere oggetti con riferimenti allo stesso modo ricorsivi, e qualsiasi algoritmo puramente ricorsivo potrebbe essere considerato in tal caso un PERICOLOSAMENTE ingenuo, in che traboccerà la profondità dello stack ricorrendo e non terminerà mai.

(beh, terminerà quando supererà la profondità dello stack, e a quel punto il tuo programma terminerà fatalmente, non quello che penso tu voglia)

In passato ho provato a serializzare - > sostituzione dei marcatori di riferimento con stringhe - > deserializzare per le mie esigenze, (spesso eseguendo il debug di backtrace con un sacco di riferimenti ricorsivi in ??esse) che sembra funzionare bene, si ottengono buchi dappertutto, ma funziona per quel compito.

Per la tua attività, se trovi che la tua matrice / struttura ha dei riferimenti ricorsivi che vi spuntano, potresti dare un'occhiata ai commenti dei contributi degli utenti qui: http://php.net/manual/en/language.references.spot.php

e quindi in qualche modo trovare un modo per contare la profondità di un percorso ricorsivo.

Potrebbe essere necessario estrarre i tuoi libri CS su algoritmi e colpire questi bambini:

(Mi dispiace per essere così breve, ma approfondire la teoria dei grafi è un po 'più che adatto a questo formato;))

Ciao Questa è una soluzione 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;
}

Dopo aver preso un po 'di ispirazione qui e dopo aver trovato questo RecursiveIteratorIterator nella documentazione di PHP, sono arrivato a questa soluzione.

Dovresti usare questo, piuttosto pulito:

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

Funziona su PHP5 e PHP7, spero che questo aiuti.

Avevo appena trovato una risposta a questa domanda quando ho notato questo post. Ecco la mia soluzione. Non l'ho provato su tonnellate di array di dimensioni diverse, ma è stato più veloce della risposta del 2008 per i dati con cui stavo lavorando con ~ 30 pezzi di profondità > 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;
}

Avviso : questo non gestisce nessun casi limite. Se hai bisogno di una soluzione solida cerca altrove, ma per il semplice caso ho scoperto che questo è piuttosto veloce.

Un'altra (migliore) modifica alla funzione di 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'aggiunta di un valore predefinito a $ childrenkey consente alla funzione di funzionare per array semplici senza chiavi per elementi figlio, vale a dire funzionerà per semplici array multidimensionali.

Questa funzione può ora essere chiamata usando:

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

o

$my_array_depth = array_depth($my_array);

quando $ my_array non ha una chiave specifica per la memorizzazione dei suoi elementi figlio.

Ecco la mia versione leggermente modificata della funzione di 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;
}
}

Cose come print array_depth ($ GLOBALS) non si guasteranno a causa della ricorsione, ma potresti non ottenere il risultato che ti aspettavi.

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 funzione proposta da Josh era decisamente più veloce:

$ 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 vecchia domanda, ma rimane rilevante per questa data. :)

Potrebbe anche contribuire una piccola modifica alla risposta di 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;
}

Ho aggiunto un secondo parametro chiamato $ childrenkey perché memorizzo gli elementi figlio in una chiave specifica.

Un esempio della chiamata di funzione è:

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

Non penso che ci sia qualcosa di incorporato. Una semplice funzione ricorsiva potrebbe facilmente scoprirlo.

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

Credo che il problema evidenziato da Kent Frederic sia cruciale. La risposta suggerita da yjerem e Asim è vulnerabile a questo problema.

Gli approcci per rientro suggeriti di nuovo da yjerem e dave1010 non sono abbastanza stabili per me perché si basano sul numero di spazi che rappresentano un rientro con la funzione print_r. Potrebbe variare con time / server / platform.

L'approccio suggerito da JoshN potrebbe essere corretto, ma penso che il mio sia più veloce:

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

Pubblica un messaggio se esegui dei test confrontando i diversi metodi. J

Credo che tu abbia dimenticato di filtrare '[' e ']' o ',' e ':' e il tipo di dati delle chiavi e dei valori dell'array. Ecco un aggiornamento del tuo array_depth più 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;
  }

Sentiti libero di migliorare il codice: D!

Penso che questo risolverebbe il problema della ricorsione e darebbe la profondità senza fare affidamento su altre funzioni php come serialize o print_r (che è al massimo rischioso e può portare a bug intrattabili):

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

Questo sembra funzionare bene per me

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

    return $depth;
}

Vorrei usare il seguente codice:

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

Un modo più veloce:

max(array_map('count', $array));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top