【问题标题】:Recursively cycle every path of an array递归循环数组的每条路径
【发布时间】:2018-05-22 07:13:08
【问题描述】:

我有以下(json)对象:

$obj = json_decode('{
    "Group1": {
        "Blue": {
            "Round": [
                "Harold",
                "Arthur",
                "Tom"
            ]
        },
        "Green": {
            "Round": [
                "Harold"
            ],
            "Circle": [
                "Todd",
                "Mike"
            ]
        }
    },
    "Group2": {
        "Blue": {
            "Round": [
                "Peter"
            ]
        }
    }
}', true);

我试图弄清楚如何递归地遍历它,以便我可以看到数组中的所有不同路径。

它可以是 4 个单独的回显或 4 行字符串。 > 可以替换为任何内容或什么都不替换。如果每行单独回显或推送到可能提供最大灵活性的数组。

Group1 - Blue - Round - (Harold, Arthur, Tom)
Group1 - Green - Round - (Harold)
Group1 - Green - Circle - (Todd, Mike)
Group2 - Blue - Round - (Peter)

我无法理解它,所以任何帮助都将不胜感激。

我想我可以以某种方式循环遍历每个喜欢:

foreach($obj as $index => $value)
{
   // and then somehow do this until you reach an array?
}

【问题讨论】:

  • 你需要遍历每个成员吗?也许你想要array_walk
  • 是的,找到每条路径。嗯,不知道如何使用深度数组,但我会尝试研究它
  • 您的对象是否始终采用Group > Color > Shapes > Names 的格式?或者可能有其他类别或其他东西?
  • 我知道有多少层深(在本例中为 4,但可能是 2,8 等),最后一个始终是一个数组,但不幸的是名称可能不同。 @欧文
  • 请发布您想要的确切输出结构。你真的想要一个 4 行字符串吗?或者你想要一个有四个元素的一维数组?你想让我们从递归函数中回显它吗?您是否希望将结果作为一组连接字符串返回?

标签: php arrays recursion multidimensional-array


【解决方案1】:

试试这个

我已经创建了示例递归程序,您可以对其进行调整

$obj = json_decode('{"Group1": {
        "Blue": {
            "Round": [
                "Harold",
                "Arthur",
                "Tom"
            ]
        },
        "Green": {
            "Round": [
                "Harold"
            ],
            "Circle": [
                "Todd",
                "Mike"
            ]
        }
    },
    "Group2": {
        "Blue": {
            "Round": [
                "Peter"
            ]
        }
    }
}');


recursive($obj);

function recursive($obj){

    if(is_array($obj)){

        foreach ($obj as $k => $v) {
            echo $v." ";
        }

        return;
    }

    foreach ($obj as $key => $value) {
        echo $key. " =>";
        recursive($value);
    }

    echo "\n";

}

以下是样本输出

Group1 =>Blue =>Round =>Harold Arthur Tom 
Green =>Round =>Harold Circle =>Todd Mike 
Group2 =>Blue =>Round =>Peter 

【讨论】:

  • 请提供预期的结果。
  • @mickmackusa 我急切地等待你的;p,这个答案不会只处理数组对象,虽然很接近。
  • (我正在努力——同时把注意力分散在我女儿身上。)@lawrence
  • 这个答案没有提供预期的结果。请注意Upvote Pixies。支持不充分的答案可确保它们留在页面上以混淆未来的读者并浪费他们的时间。
  • 我现在需要对这个不充分的答案投反对票。由于有两个正确的答案,这个答案只是噪音,不会使未来的读者受益。
【解决方案2】:

这适用于不同深度的数组,请查看live demo

while(count($array) != count($array, 1))    // stop when $array is one dimension
{
    $temp = [];
    foreach($array as $k => $v)
    {
        if(is_array($v))
        {
            if(count($v) != count($v, 1))  // check if reached the inner most array
            {
                foreach($v as $kk => $vv)
                {
                    $temp[$k . ' - ' . $kk] = $vv;
                }
            }
            else
                $temp[$k] = '(' . implode($v, ', ') . ')';
        }else
            $temp[$k] = $v;
    }
    $array = $temp;
}

foreach($array as $k => $v)
    echo $k . ' - ' . $v . "\n";

注意:

traverse array from outer to inner
traverse array from inner to outer

【讨论】:

  • 假设这是更准确的。+1
  • @mickmackusa,很抱歉没有立即回复您的评论。我改进了我的答案。你现在可以检查一下。如果是这样,请删除反对票。谢谢。
  • 对于有人反对我的回答,我首先为我之前的回答道歉,这是不充分的。但现在我已经改进了它,你能取消你的反对票吗,因为它现在有效。
  • 伟大的堆叠技术。我打算走这条路,与其他人不同。反转和 +1。
【解决方案3】:

仅在给定样本上进行测试。但是,如果阵列级别增加,这应该可以工作。我主要使用RecursiveIteratorIterator类函数

// Initialize RecursiveIteratorIterator
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($obj), RecursiveIteratorIterator::SELF_FIRST);
$paths = array(); // Paths storage
foreach ($iterator as $k => $v) { // Loop thru each iterator

    if (!$iterator->hasChildren()) { // Check if iterator hasChildren is false
        $innermost = $iterator->getSubIterator($iterator->getDepth()); // Get innermost child which is the array
        for ($p = array(), $i = 0, $z = $iterator->getDepth(); $i <= $z; $i++) { // Loop and push each path to the innermost array
            $p[] = $iterator->getSubIterator($i)->key();
        }
        array_pop($p); // Remove key
        $innermost = (array)$innermost; // Cast innermost to array
        $p[] = '(' . implode(', ', $innermost) . ')'; // push formatted innermost array to path
        $path = implode(' - ', $p); // implode the path
        $paths[] = $path; // store to list of paths array
    }

}

$paths = array_unique($paths); // remove exactly same paths

foreach ($paths as $value) {  // Loop and echo each path
    echo $value.'<br>';
}

输出:- https://eval.in/915070

【讨论】:

    【解决方案4】:

    我创建了简单的递归函数。以您为例。将先前的键值存储在一个变量中,并根据先前的键添加所有数据并创建新数组。(其中包含所有先前的键作为索引,最后一个元素作为值)。试试下面的代码:

    $obj = json_decode('{
        "Group1": {
            "Blue": {
                "Round": [
                    "Harold",
                    "Arthur",
                    "Tom"
                ]
            },
            "Green": {
                "Round": [
                    "Harold"
                ],
                "Circle": [
                    "Todd",
                    "Mike"
                ]
            }
        },
        "Group2": {
            "Blue": {
                "Round": [
                    "Peter"
                ]
            }
        }
    }', true);
    
    function traverse_array($array,$key="",$prev="",&$final_op=array())
    {
      if(is_array($array))
      {
        $prev .= $key." - ";
        foreach ($array as $key => $value) {
          traverse_array($value,$key,$prev,$final_op);
        }
      }
      else
      {
    
        $prev =trim($prev," - ");            
        $final_op[$prev][]=$array;
      }
      return $final_op;
    }
    $data = traverse_array($obj);
    foreach ($data as $key => $value) {
      echo $key." (".implode(",", $value).")";
      echo PHP_EOL;
    }
    

    DEMO

    【讨论】:

      【解决方案5】:

      我已将我的方法归结为非常简单易读的内容。有关解释,请参见内联 cmets。我有两个版本:一个 4 行回显函数和一个数组返回函数。

      *注意这个方法是专门为这个问题构建的,因为:

      • 输入数组的深度总是至少为两层,并且

      方法 #1 - 从函数内部回显字符串: (Demo)

      $array=json_decode('{
          "Group1": {
              "Blue": {
                  "Round": {
                      "One": [
                          "Lawrence",
                          "Anant",
                          "B."
                      ],
                      "Two": [
                          "Erwin"
                      ]
                  }
              },
              "Green": [
                 "Bryan",
                 "Mick"
              ]
          },
          "Group2": [
              "Peter",
              "Kris"
          ]
      }', true);
      
      function recurse($array,$path=''){
          foreach($array as $k=>$v){
              if(!is_array(current($v))){  // check type of the first sub-element's value
                  echo ($path?"$path > ":''),"$k > (".implode(', ',$v).")\n";  // don't recurse, just implode the indexed elements
              }else{                                                           // recurse because at least 2 levels below
                  recurse($v,($path?"$path > $k":$k));                         // build path appropriately
              }
          }
      }
      recurse($array);
      

      输出:

      Group1 > Blue > Round > One > (Lawrence, Anant, B.)
      Group1 > Blue > Round > Two > (Erwin)
      Group1 > Green > (Bryan, Mick)
      Group2 > (Peter, Kris)
      

      方法 #2 - 返回一个 4 元素的路径数组: (Demo)

      function recurse($array,$path='',&$result=[]){
          foreach($array as $k=>$v){
              if(!is_array(current($v))){  // check type of the first sub-element's value
                  $result[]=($path?"$path > ":'')."$k > (".implode(', ',$v).')';  // don't recurse, just implode the indexed elements
              }else{  // recurse because at least 2 levels below
                  recurse($v,($path?"$path > ":'').$k,$result);  // build path appropriately
              }
          }
          return $result;
      }
      var_export(recurse($array));
      

      输出:

      array (
        0 => 'Group1 > Blue > Round > One > (Lawrence, Anant, B.)',
        1 => 'Group1 > Blue > Round > Two > (Erwin)',
        2 => 'Group1 > Green > (Bryan, Mick)',
        3 => 'Group2 > (Peter, Kris)',
      )
      

      最后一个更新:

      我能给出的最佳建议是去掉这个中间步骤,只需将 your raw/initial json string 转换为您想要的新输出(不需要递归/堆叠):

      代码:(Demo)

      $seperateArray = json_decode('[
      { "tier1": "Group1", "tier2": "Blue", "tier3": "Round", "tier4": "One", "tier5": "Lawrence" },
      { "tier1": "Group1", "tier2": "Blue", "tier3": "Round", "tier4": "One", "tier5": "Anant" },
      { "tier1": "Group1", "tier2": "Blue", "tier3": "Round", "tier4": "One", "tier5": "B." },
      { "tier1": "Group1", "tier2": "Blue", "tier3": "Round", "tier4": "Two", "tier5": "Erwin" },
      { "tier1": "Group1", "tier2": "Green", "tier3": "Bryan" },
      { "tier1": "Group1", "tier2": "Green", "tier3": "Mick" },
      { "tier1": "Group2", "tier2": "Peter" },
      { "tier1": "Group2", "tier2": "Kris" }]',true);
      
      foreach($seperateArray as $row){
          $last_val=current(array_splice($row,-1));  // extract last element, store as string
          $results[implode(' > ',$row)][]=$last_val;
      }
      foreach($results as $k=>$v){
          echo "$k > (",implode(', ',$v),")\n";
      }
      // same output as earlier methods
      

      【讨论】:

        【解决方案6】:

        这是我对生成器函数的看法:

        function paths(array $a)
        {
            // if first item is an array, recurse
            if (is_array(reset($a))) {
                foreach ($a as $k => $v) {
                    foreach (paths($v) as $path) {
                        // yield "key - subpath"
                        yield sprintf('%s - %s', $k, $path);
                    }
                }
            } else {
                // yield leaf
                yield sprintf('(%s)', implode(', ', $a));
            }
        }
        
        foreach (paths($obj) as $path) {
            printf("%s\n", $path);
        }
        

        试试online

        【讨论】:

        • 抱歉@Joe 显然迟到/精炼的答案在此页面上没有得到承认。我的回答迟到了 24 小时,尽管我对所有其他正确答案都投了赞成票,但没有任何回应。 * 并不总是一个正义之地。