递归地遍历数组并打印遍历的路径

发布于 2024-12-07 06:07:04 字数 2797 浏览 0 评论 0原文

有人可以帮助我编写一些代码或说明,了解如何递归遍历数组以及在到达最后一个元素时打印其完整路径吗?一个简单的 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, 

Can someone help me with some code or instructions on how to walk recursively an array and when reaching the last element print the full path to it? A simple echo will work because I will adapt the code to some other function I'm developing.

The function doesn't need to figure the array dimension because this param will be passed:

Example:

$depth = 8;

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

When function reachs the 8th element it print all the path to it:

//print path
'1 -> 3 -> 5 -> 6 -> 9 -> 5 -> 8 -> 9'
  • As I said, only printing in this format will work cause I will implement the code into some other function.

  • array keys can have the same value. Obviously not the same value in the same sequence for the entire arary.

Updated:

Walk recursively function:

$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 " "; }
                echo $k . " : " . "<br>";
                listArrayRecursive($v, $ident + 1);
            }else{
                for ($i=0; $i < $ident * 10; $i++){ echo " "; }
                echo $k . " : " . $v . "<br>";
            }
        }
    }else{
        echo "Variable = " . $array_name;
    }
}

listArrayRecursive($someArray);

Will print:

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

Now, how can I also print the path of the array everytime it reaches the end? For example:

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

EDITED CODE ADDING A THIRD PARAM TO RECORD THE PATH:

$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 " "; }
            echo $k . " : " . "<br>";
            $path .= $k . ', ';
            listArrayRecursive($v, $ident + 1, $path);
        }else{
             for ($i=0; $i < $ident * 10; $i++){ echo " "; }
             echo $k . " : " . $v . ' - path -> ' . $path . "<br>";
        }
    }
}

listArrayRecursive($someArray);

Will print:

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, 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

随梦而飞# 2024-12-14 06:07:05
<?php
function printListRecursive($a, $var='', $i = 0) {
    if (!is_array($a)) {
        $var .= $a;
        return $var;
    }
    $string = "";
    foreach ($a as $k => $value) {
        $string .= str_repeat("  ", $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;
?>

在此处检查代码

<?php
function printListRecursive($a, $var='', $i = 0) {
    if (!is_array($a)) {
        $var .= $a;
        return $var;
    }
    $string = "";
    foreach ($a as $k => $value) {
        $string .= str_repeat("  ", $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

渔村楼浪 2024-12-14 06:07:05

我根据@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->hasChildren() 如果当前迭代的叶子是一个对象,则返回 true。因此,我无法以这种方式获得它的路径。

I came up with the following function based on @salathe's one. It returns an array where each element is an array containing the leaf at index 0 and the array of the path keys at index 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;
}

The main reason I implemented this one is that $iterator->hasChildren() returns true if the current iterated leaf is an object. Therefore, I wouldn't be able to get the path of it that way.

青衫儰鉨ミ守葔 2024-12-14 06:07:05

我找到了这个解决方案,它还考虑了结构元素是否为数组:

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

I found this solution, which also keeps into account if elements of the structure are arrays:

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

Example output:

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

In case anybody is interested, here it is the port to 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")
時窥 2024-12-14 06:07:05

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

You can add a third parameter which holds the actual path as String. At the end you can output it then.

凉墨 2024-12-14 06:07:04

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

function listArrayRecursive($someArray) {
    $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($someArray), RecursiveIteratorIterator::SELF_FIRST);
    foreach ($iterator as $k => $v) {
        $indent = str_repeat(' ', 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>";
        }
    }
}

You could employ a RecursiveIteratorIterator (docs) to take the hard work out of recursing through the arrays.

function listArrayRecursive($someArray) {
    $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($someArray), RecursiveIteratorIterator::SELF_FIRST);
    foreach ($iterator as $k => $v) {
        $indent = str_repeat(' ', 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>";
        }
    }
}
等数载,海棠开 2024-12-14 06:07:04

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

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。

This example is to give you idea, not to solve the actual task.

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');

If no match is found, null is returned.

稀香 2024-12-14 06:07:04
$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;
}
$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;
}
烦人精 2024-12-14 06:07:04

我刚刚编写了一个函数,使递归循环变得更容易:
与 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 将导致它遍历整个数组。

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

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

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

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

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

I just wrote a function that makes recursive looping a bit easier:
Similar to array_walk_recursive but with some extra functionality

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

Called like so:

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

Setting recursive to false will only evaluate the first level.

Setting recursive to true will cause it to traverse the entire array.

Setting recursive to an integer will cause it to only traverse to that depth.

Walkable functions can be referenced or passed to callback as anonymous function.

(expects: value, key, custom, info)
The returned value replace the current value.

Custom data can be passed and some additional info is provided for you.

You can expand on the walk function if you need additional info.

心凉 2024-12-14 06:07:04

我有类似的问题。这是一个深度优先搜索的解决方案(不包括路径深度,它到达数组的最后)。如果您不想包含该值,请注释“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);
        }
    }
}

I had similar problem. Here is a Depth-First Search-ish solution(no path depth included, it reaches until the very end of the array). Comment the 'if' statement if u don't want to include the value:

$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);
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文