web-dev-qa-db-ja.com

PHP配列がどのくらい「深い」かを調べる方法はありますか?

A PHP配列はその要素の配列を持つことができます。また、それらの配列は配列などを持つことができます。PHP array?例は、初期配列に要素として配列がない場合は1、少なくとも1つの要素が配列である場合は2などを返す関数です。

49
Thomas Owens

これはそれを行う必要があります:

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

?>

編集:非常に迅速にテストし、機能しているように見えます。

61
Jeremy Ruten

ケントフレドリックが指摘した問題を回避する別の方法を次に示します。 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;
}
73
Jeremy Ruten

再帰的に行う例に注意してください

PHPは、その配列の他の場所への参照を持つ配列を作成でき、同様に再帰的な参照を持つオブジェクトを含めることができ、そのような場合は純粋に再帰的なアルゴリズムを考慮することができます[〜#〜] dangerously [〜 #〜]単純なもの。スタックの深さの再帰をオーバーフローさせ、終了することはありません。

(まあ、スタックの深さを超えると終了し、その時点でプログラムは致命的に終了しますが、あなたが望むものではありません)

過去に、私はシリアル化を試みました->参照マーカーを文字列に置き換え->私のニーズのためにデシリアライズします(多くの場合、再帰的な参照の負荷でバックトレースをデバッグします) 。

あなたのタスクについて、配列/構造に再帰的な参照が含まれていることがわかった場合は、ユーザーが投稿したコメントを参照してください: http://php.net/manual/en/language .references.spot.php

そして、どういうわけか再帰的なパスの深さを数える方法を見つけます。

あなたはアルゴリズムに関するCSの本を出して、これらの赤ちゃんを打つ必要があるかもしれません:

(非常に短いので申し訳ありませんが、グラフ理論を掘り下げることは、この形式に適しておりません;))

43
Kent Fredric

こんにちはこれは代替ソリューションです。

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

ここで少しインスピレーションを取り、これを見つけた後 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の両方で動作します。これが役立つことを願っています。

7
TwystO

この投稿に気付いたとき、この質問に対する答えを考え出したところです。これが私の解決策でした。さまざまなサイズのアレイでこれを試したことはありませんが、深さ4を超える30個のデータで作業していたデータの2008年の回答よりも高速でした。

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

警告:これは処理しませんanyエッジケース。堅牢なソリューションが必要な場合は別の場所を探しますが、単純なケースでは、これは非常に高速であることがわかりました。

4
fncomp

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

デフォルト値$ childrenkeyに追加すると、子要素のキーがない単純な配列で機能することができます。つまり、単純な多次元配列で機能します。

この関数は、次を使用して呼び出すことができます。

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

または

$my_array_depth = array_depth($my_array);

$ my_arrayに、その子要素を格納するための特定のキーがない場合。

3
Amir Syafrudin

ジェレミー・ルーテンの関数を少し変更したバージョンを次に示します

_// 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)のようなものは再帰によりエラーにはなりませんが、期待した結果が得られない場合があります。

3
dave1010

古い質問ですが、この日付に関連したままです。 :)

ジェレミー・ルーテンからの回答に若干の修正を加えることもできます。

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という2番目のパラメーターを追加しました。

関数呼び出しの例は次のとおりです。

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

Joshが提案した機能は、間違いなく高速でした。

$ 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

ケント・フレデリックが強調した問題は重要だと思います。 yjeremとAsimによって提案された答えは、この問題に対して脆弱です。

Yjeremとdave1010によって再び提案されたインデントによるアプローチは、print_r関数でインデントを表すスペースの数に依存しているため、私にとって十分に安定していません。 time/server/platformによって異なる場合があります。

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

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

これは私にとってはうまくいくようです

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

これは再帰の問題を解決し、serializeやprint_rのような他のphp関数に依存せずに深さを与えると思います(せいぜいリスクがあり、扱いにくいバグにつながる可能性があります)。

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

「[」と「]」または「、」と「:」、および配列のキーと値のデータ型をフィルタリングするのを忘れたと思います。 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!

1
cmosversion

何かが組み込まれているとは思いません。単純な再帰関数は簡単に見つけることができます。

1
KernelM

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

注:配列にオブジェクトがある場合、これは機能しません。

0
Narek

次のコードを使用します。

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