Walk array recursively and print the path of the w

2019-01-14 13:29发布

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 "&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);

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 "&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);

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, 

8条回答
我命由我不由天
2楼-- · 2019-01-14 13:42

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);
        }
    }
}
查看更多
男人必须洒脱
3楼-- · 2019-01-14 13:43

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

查看更多
SAY GOODBYE
4楼-- · 2019-01-14 13:45

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.

查看更多
不美不萌又怎样
5楼-- · 2019-01-14 13:54
<?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

查看更多
仙女界的扛把子
6楼-- · 2019-01-14 13:55
$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;
}
查看更多
冷血范
7楼-- · 2019-01-14 13:58

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('&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>";
        }
    }
}
查看更多
登录 后发表回答