【问题标题】:Walk array recursively and print the path of the walk递归遍历数组并打印行走的路径
【发布时间】:2011-11-27 06:53:04
【问题描述】:

有人可以帮助我提供一些关于如何递归遍历数组以及在到达最后一个元素时打印到它的完整路径的代码或说明吗?一个简单的 echo 就可以了,因为我会将代码调整为我正在开发的其他功能。

函数不需要计算数组维度,因为这个参数会被传递:

例子:

$depth = 8;

$array[1][3][5][6][9][5][8][9];

当函数到达第 8 个元素时,它会打印到它的所有路径:

//print path
'1 -> 3 -> 5 -> 6 -> 9 -> 5 -> 8 -> 9'
  • 正如我所说,只有以这种格式打印才有效,因为我会将代码实现到其他函数中。

  • 数组键可以具有相同的值。对于整个数组,显然不是同一个序列中的同一个值。

更新:

递归遍历函数:

$someArray[1][2][3] = 'end';
$someArray[1][2][6] = 'end';
$someArray[1][3][6] = 'end';
$someArray[4][3][7] = 'end';

function listArrayRecursive(&$array_name, $ident = 0){
    if (is_array($array_name)){
        foreach ($array_name as $k => &$v){
            if (is_array($v)){
                for ($i=0; $i < $ident * 10; $i++){ echo "&nbsp;"; }
                echo $k . " : " . "<br>";
                listArrayRecursive($v, $ident + 1);
            }else{
                for ($i=0; $i < $ident * 10; $i++){ echo "&nbsp;"; }
                echo $k . " : " . $v . "<br>";
            }
        }
    }else{
        echo "Variable = " . $array_name;
    }
}

listArrayRecursive($someArray);

将打印:

1 :
      2 :
                3 : end
                6 : end
      3 :
                6 : end
4 :
      3 :
                7 : end

现在,我怎样才能在每次到达末尾时打印数组的路径?例如:

1 :
      2 :
                3 : end : path -> 1,2,3
                6 : end : path -> 1,2,6
      3 :
                6 : end : path -> 1,3,6
4 :
      3 :
                7 : end : path -> 4,3,7

添加第三个参数以记录路径的编辑代码:

$someArray[1][2][3] = 'end';
$someArray[1][2][6] = 'end';
$someArray[1][3][6] = 'end';
$someArray[4][3][7] = 'end';
$someArray[3][2] = 'end';

function listArrayRecursive(&$array_name, $ident = 0, $path = null){
     foreach ($array_name as $k => &$v){
         if (is_array($v)){
            for ($i=0; $i < $ident * 10; $i++){ echo "&nbsp;"; }
            echo $k . " : " . "<br>";
            $path .= $k . ', ';
            listArrayRecursive($v, $ident + 1, $path);
        }else{
             for ($i=0; $i < $ident * 10; $i++){ echo "&nbsp;"; }
             echo $k . " : " . $v . ' - path -> ' . $path . "<br>";
        }
    }
}

listArrayRecursive($someArray);

将打印:

1 :
          2 :
                    3 : end - path -> 1, 2,
                    6 : end - path -> 1, 2,
          3 :
                    6 : end - path -> 1, 2, 3,
4 :
          3 :
                    7 : end - path -> 1, 4, 3,
3 :
          2 : end - path -> 1, 4, 3, 

【问题讨论】:

  • 嗯,用递归解决是基本任务。你试过什么?
  • @zerkms 实际上我有这个递归代码codepad.org/iyrcdfQP 但我坚持跟踪当前 $key 的路径。
  • 你能举一个更清楚的例子吗,如果数组的任何给定深度内的多个项目,或者如果数组没有像$depth 那样嵌套那么深?这解决了什么问题?
  • @NullUserExceptionఠ_ఠ 是的,它永远不会有那么深,只是想说明一下。当我开始使用脚本时,我会遇到的最大深度可能是 3 个级别。如果你检查上面评论中的代码,你会看到我在做什么。

标签: php arrays recursion multidimensional-array


【解决方案1】:

我找到了这个解决方案,如果结构的元素是数组,它也会考虑到:

$file_contents=file_get_contents("data.json");
$json_dump=json_decode($file_contents); 
printPath($json_dump, '', "" ,"");

function printPath($the_array, $path, $prevType) {
// Parse all elements of a structure 
// and print full PHP path to each one.
    foreach($the_array as $key => $value)  {
        if(is_array($value)) {
            // Array element cannot be directly printed: process its items as     objects:
            printPath($value, $path  . $key , "array");
        } else {            
            if (!is_object($value))  { // If the element is not an object, it can be printed (it's a leaf)
                if(is_string($value)) { 
                    $finalValue = '"' . $value . '"'; 
                } else { 
                    $finalValue = $value;
                }
                if($prevType == "array") {
                    // If array element, add index in square brackets
                    echo($path  . "["  . $key . "] =  " .  $finalValue . "<br>");
                } else {
                    echo($path . $key  . " = " . $finalValue . "<br>");                     
                }   

            } else { // else store partial path and iterate:
                if($prevType == "array") {
                    // Path of array element must contain element index:  
                    printPath($value, $path . "["  . $key . "]->"  , "dummy");
                } else {
                    printPath($value, $path . $key . "->", "dummy");
                }           
            }
        }
    }
}

示例输出:

status->connections->DSS-25->band = "X"
status->connections->DSS-25->endAt = "2019-11-20T20:40:00.000Z"
status->connections->DSS-25->startAt = "2019-11-20T12:40:00.000Z"
geometry[0]->obs[0]->name = "UDSC64"
geometry[0]->obs[0]->hayabusa2->azm = 90.34
geometry[0]->obs[0]->hayabusa2->alt = -20.51

如果有人感兴趣,这里是 Javascript 的端口:

function iterate(obj, stack, prevType) {
    for (var property in obj) {
        if ( Array.isArray(obj[property]) ) {
            //console.log(property , "(L="  + obj[property].length + ") is an array  with parent ", prevType, stack);
            iterate(obj[property], stack  + property , "array");
        } else {
            if ((typeof obj[property] != "string")  && (typeof obj[property] != "number"))  {
                if(prevType == "array") {
                    //console.log(stack + "["  + property + "] is an object, item of " , prevType, stack);
                    iterate(obj[property], stack + "["  +property + "]." , "object");
                } else {
                    //console.log(stack +    property  , "is " , typeof obj[property] , " with parent ", prevType, stack );
                    iterate(obj[property], stack  + property + ".", "object");
                }   
            } else {
                if(prevType == "array") {
                    console.log(stack + "["  + property + "] =  "+  obj[property]);

                } else {
                    console.log(stack +    property  , " =  " ,  obj[property] );                       
                }   
            }
        }
    }
}

iterate(object, '', "File")

【讨论】:

    【解决方案2】:

    我在@salathe 的基础上提出了以下功能。它返回一个数组,其中每个元素都是一个数组,其中包含索引 0 处的叶子和索引 1 处的路径键数组:

    function loosenMultiDimensionalArrayPathForEachVal($array) {
        $iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array), \RecursiveIteratorIterator::SELF_FIRST);        
        $iterator->rewind();
        $res = [];
        foreach ($iterator as $v) {
            $depth = $iterator->getDepth();
            for ($path = array(), $i = 0, $z = $depth; $i <= $z; $i++) {
                $path[] = $iterator->getSubIterator($i)->key();
            }
            $leaf = $array;
            foreach ($path as $pathKey) {
                $leaf = $leaf[$pathKey];
            }
            if (!is_array($leaf)) {
                $res[] = [
                    $v,
                    $path
                ];
            }
        }
        return $res;
    }
    

    我实现这个的主要原因是$iterator-&gt;hasChildren() 返回true 如果当前迭代的叶子是一个对象。因此,我将无法以这种方式获得它的路径。

    【讨论】:

    • 致命错误:不能使用 stdClass 类型的对象作为第 xxxx 行中的数组
    • 您可以使用此功能发布您的代码,以便我可以尝试复制它吗?
    • $file_contents=file_get_contents("data.json"); $json_dump=json_decode($file_contents); listArrayRecursive($json_dump);松开MultiDimensionalArrayPathForEachVal($json_dump); --------------------- 第一次调用正常,第二次调用出错。
    • 感谢您提供样品。将json_decode的第二个参数bool $assoc(默认为false)设置为true,即可得到预期的输出,即:json_decode($file_contents, true);loosenMultiDimensionalArrayPathForEachVal 假设您传递给它的是一个多维数组,而不是 stdClass 对象。如果有帮助,您可以为我的回答投票。
    • 以这种方式,它只是不打印任何东西(但 listArrayRecursive() 继续工作)。它被执行了,因为如果我在这里和那里放置 ECHO“调试”,它就会打印“调试”......但这就是它所做的一切。
    【解决方案3】:
    <?php
    function printListRecursive($a, $var='', $i = 0) {
        if (!is_array($a)) {
            $var .= $a;
            return $var;
        }
        $string = "";
        foreach ($a as $k => $value) {
            $string .= str_repeat("&nbsp;&nbsp;", $i) .' - '. $k . ':';
            if (!is_array($value)) {
                $string .= $value . '<br />';
            } else {
                $string .= '<br />';
                $string .= printListRecursive($value, $var, $i + 1);
            }
        }
        return $string;
    }
    $test_array = [
        'America' => [
            'Argentina' => 'Buenos Aires',
            'Peru' => 'Lima'
        ],
        'Europe' => [
            'Ireland' => 'Dublin',
            'France' => 'Paris',
            'Italy' => 'Rome'
        ]
    ];
    $result = printListRecursive($test_array);
    echo $result;
    ?>
    

    Check code here

    【讨论】:

    • 沙盒演示不允许运行。请edit您的回答以提供当前有效的沙箱。
    【解决方案4】:

    我有类似的问题。这是一个深度优先搜索的解决方案(不包括路径深度,它一直到数组的最后)。如果您不想包含该值,请评论“if”语句:

    $output = array();
    retrievePath($someArray, $output);
    
    function retrievePath($someArray, array &$pathKeeper)
    {
        if(!is_array($someArray)){ // $someArray == "end"
            $element = array_pop($pathKeeper) ?? '';// if the array is empty pop returns null, we don't want that
            array_push($pathKeeper, $element . '->'. $someArray);
        } else{
            end($someArray);//we want to get the last element from the array so we move the internal pointer to it's end
            $endElKey = key($someArray);//take the key where the pointer is
            reset($someArray);
            foreach($someArray as $key=>$value){
                $element = array_pop($pathKeeper);
                array_push($pathKeeper, $element === null ? $key : $element . '->' . $key);// we don't want '->' at the beginning
                retrievePath($value, $pathKeeper);
                if($key != $endElKey) //we check whether this is not the last loop
                    array_push($pathKeeper, $element);
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      我刚刚写了一个函数,使递归循环更容易一些: 类似于 array_walk_recursive 但有一些额外的功能

      public static function walk($array, $callback, $custom = null, $recursive = false, $info = [])
      {
          $r = $recursive;
          if (gettype($r) === 'integer') {
              $r--;
          }
          $info['depth'] = empty($info)?1:$info['depth'] + 1;
          $info['count'] = count($array);
          $info['i'] = 1;
          foreach($array as $k => $v) {
              if (is_array($v) && $r > 0) {
                  $array[$k] = static::walk($v, $callback, $custom, $r, $info);
              } else {
                  $array[$k] = $callback($v, $k, $custom, $info);
              }
              $info['i'] ++;
          }
          return $array;
      }
      
      public static function walkable($v, $k, $custom, $info)
      {
          if (is_string($v)) {
              return $v." [ custom: {$custom['key']} ] [ level: ".$info['depth'].' | No '.$info['i'].' of '.$info['count']." ]";
          }
          return $v;
      }
      

      这样称呼:

         $result = Namespace\ClassName::walk($array, ['Namespace\ClassName', 'walkable'], ['key'=>'value'], true);
      

      将 recursive 设置为 false 只会评估第一级。

      将recursive设置为true会导致遍历整个数组。

      将递归设置为整数将导致它仅遍历该深度。

      Walkable 函数可以作为匿名函数被引用或传递给回调。

      (期望:值、键、自定义、信息) 返回值替换当前值。

      可以传递自定义数据,并为您提供一些附加信息。

      如果您需要更多信息,可以扩展 walk 功能。

      【讨论】:

        【解决方案6】:

        您可以使用RecursiveIteratorIterator (docs) 来消除递归遍历数组的繁重工作。

        function listArrayRecursive($someArray) {
            $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($someArray), RecursiveIteratorIterator::SELF_FIRST);
            foreach ($iterator as $k => $v) {
                $indent = str_repeat('&nbsp;', 10 * $iterator->getDepth());
                // Not at end: show key only
                if ($iterator->hasChildren()) {
                    echo "$indent$k :<br>";
                // At end: show key, value and path
                } else {
                    for ($p = array(), $i = 0, $z = $iterator->getDepth(); $i <= $z; $i++) {
                        $p[] = $iterator->getSubIterator($i)->key();
                    }
                    $path = implode(',', $p);
                    echo "$indent$k : $v : path -> $path<br>";
                }
            }
        }
        

        【讨论】:

        • 这是一个很好的解决方案,但您必须小心,因为如果您的叶子是一个对象,$iterator-&gt;hasChildren() 返回 true,并且代码实际上不会打印导致叶子对象的路径。我在答案中找到了解决方法
        • 终于找到了我的问题的答案!但是我仍然错过了最后一件事:如何在方括号而不是逗号中打印数组索引? geometry,5,obs,0,latitude 应该是 geometry[5]->obs[0]->latitude
        【解决方案7】:

        您可以添加第三个参数,将实际路径保存为字符串。最后就可以输出了。

        【讨论】:

        • 已经尝试过了,但没那么简单。检查我刚刚编辑的代码。有什么建议吗?
        【解决方案8】:
        $a= array(1,2,3,4,5,6);
        $val = end($a);
        print_array($a,$val);
        function print_array(&$arr, $val)
        {
            if ($val === false)
                return;
        
            $curr = prev($arr);
            print_array($arr,$curr);
            echo $val;
        }
        

        【讨论】:

          【解决方案9】:

          这个例子是给你一个想法,而不是解决实际的任务。

          function recursiveSearch($array,$search){
              foreach($array as $key=>$val){
                  if($val==$search)return $key;
                  $x=recursiveSearch($array[$key],$search);
                  if($x)return $key.' -> '.$x;
              }
          }
          
          echo recursiveSearch($array,'search');
          

          如果没有找到匹配项,则返回 null。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-01-04
            • 1970-01-01
            • 2016-11-10
            • 2021-05-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-11-13
            相关资源
            最近更新 更多