【问题标题】:Recursively iterate through array and create an array based on it's "path"递归遍历数组并根据它的“路径”创建一个数组
【发布时间】:2016-11-10 10:28:15
【问题描述】:

我很难弄清楚如何递归地遍历一个数组并创建一个它的“路径”数组

我有两个数组。一个是目录树的数组,看起来像这样:

$directoryTree = [

    'accounting' => [

        'documents' => [
        ],

        'losses' => [
        ],

        'profit' => [
        ]               

    ],
    'legal' => [

        'documents' => [
        ]
    ]
];

另一个是指定文件应该驻留在哪个目录“路径”的文件列表:

$fileList = [

    [
        'name' => 'Overview.doc',
        'dir_path' => []
    ],

    [
        'name' => 'Incorporation.doc',
        'dir_path' => []
    ],  

    [
        'name' => 'Profit And Loss.xls',
        'dir_path' => ['accounting']
    ],

    [
        'name' => 'Profit 1.xls',
        'dir_path' => ['accounting', 'profit']
    ],

    [
        'name' => 'Loss 1.xls',
        'dir_path' => ['accounting', 'losses']
    ],

    [
        'name' => 'TOS Draft.doc',
        'dir_path' => ['legal', 'documents']
    ]

    [
        'name' => 'Accounting Doc.pdf',
        'dir_path' => ['accounting', 'documents']
    ],      
];

基本上我要做的是遍历$directoryTree 并查看$fileList 中是否有任何元素具有迭代器所在的“路径”。如果有元素应该添加在那里。

最终的数组应该类似于this:

$finalOutput = [
    'accounting' => [

        'documents' => [
            'Accounting Doc.pdf'
        ],

        'losses' => [
            'Loss 1.xls'
        ],

        'profit' => [
            'Profit 1.xls'
        ],

        'Profit And Loss.xls'

    ],
    'legal' => [

        'documents' => [
            'TOS Draft.Doc'
        ]
    ],

    'Overview.doc',
    'Incorporation.doc',

];

我还包括这张图片只是为了进一步澄清:


我所做的尝试并没有真正让我走得很远。我在尝试递归遍历数组时一直卡住,不知道接下来如何解决这个问题。

【问题讨论】:

  • 尾递归,传递一个保留先前遍历的元素名称的变量,并在向下搜索时附加下一个。
  • 当你的树中不存在路径时你会怎么做?创建还是不添加文件? (另外你可能想做这样的事情:3v4l.org/Ai9Uh
  • 应该总是有路径。如果路径不存在,我可能希望抛出异常。请将您的解决方案作为答案发布,以便我在测试后接受它

标签: php arrays recursion iterator


【解决方案1】:

有趣的问题。我的回答实际上并没有从“怎么做”的角度回答这个问题,但它表明您需要将复杂的问题分解为更简单的一次,以便更容易解决,并且这些小解决方案可能以后可以重复使用。

基本上,您需要能够设置“嵌套键”的值。我遇到过questions(我自己也遇到过)关于获取“嵌套键”值的问题。

所以我决定实现隐藏递归并帮助使用类似数组的键来获取嵌套值的数组访问类。您可以查看repo。代码的解释实际上适合问题的领域,但由于它很长,我无法在这里解释。重点是数组访问方法隐藏了递归。

鉴于这个类,您的问题可以简化为$fileList 上的简单循环:

$directoryTree = new CompositeKeyArray($directoryTree);

foreach ($fileList as $file) {
    if (
        !empty($file['dir_path'])
        && !isset($directoryTree[$file['dir_path']])
    ) {
        throw new Exception;
    }

    array_push($file['dir_path'], []);
    $directoryTree[$file['dir_path']] = $file['name'];
}

var_dump($directoryTree->toArray()); 

在其中一个 cmets 中,您提到您希望在不存在的路径上引发异常。 if 部分处理这个问题。

这里是working demo

【讨论】:

    【解决方案2】:

    遍历$fileList 并将它们放在正确的目录数组下不是更容易吗?例如

    $finalOutput = $directoryTree;
    foreach ($fileList as $file) {
        $current_dir &= $finalOutput;
        foreach ($file['dir_path'] as $dir) {
            if (isset($current_dir[$dir])) {
                $current_dir &= $current_dir[$dir];
            } else {
                $current_dir = null;
                break;
            }
        }
        if (is_array($current_dir)) {
            $current_dir[] = $file['name'];
        } else {
            // do you want to do anything if dir is not there?
        }
    }
    

    注意:我还没有运行代码,但应该让你知道它是如何工作的。

    如果出于某种未说明的原因,无论如何您必须遍历$directoryTree,您应该小心确保逻辑在O(n)(或附近)而不是O(n^2) 时间运行。一种简单的算法如下:

    • 运行$fileList并生成所有目录的映射 -> 文件
    • 现在运行 $directoryTree 并填充地图中的文件

    【讨论】:

      【解决方案3】:

      下面的代码应该可以解决问题。不过考虑一下它的伪代码。真的是javascript、java、words的组合哈哈。

      @stck 是一个字符串数组,跟踪路径

      //function should return an empty $fileList signaling all files have been processed
      function treeTraversal($directoryTree, $fileList, stck) {
      
          //begins by processing all the files that are children of the root directory 
          //e.g. those with dir_name =[]
          //removes them from $fileList so we don't have to keep iterating over them
          if(stck.length == 0) {// == 0 only once, when processing first child of root
              for(var i = 0; i < $fileList.length; i ++) {
                  if($fileList[i].dir_name.length == 0) {
                      $directoryTree.push($fileList[i]);
                      $fileList.removeElementAtIndex(i)
                  }
              }
          }
      
          // now recursively traverse the tree. Record the path in var called stck
          for(var i = 0; i < $directoryTree.length; i++) {
              stck.push($directoryTree[i]); // $directoryTree[i] refers to name of directory not its contents
              if($directoryTree[i] is array)
                  $fileList = treeTraversal($directoryTree[i], $fileList, stck);
          for(var j = 0; j < $fileList.length; j++) {
              if($fileList[j].dir_path == stck) {
                  $directoryTree.push($fileList[j]);
                  $fileList.removeElementAtIndex(j);
              }
          }
          stck.pop();
          return $fileList
      }
      

      【讨论】:

        猜你喜欢
        • 2011-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-13
        • 2012-06-23
        • 2023-03-23
        相关资源
        最近更新 更多