【问题标题】:transform file/directory structure into 'tree' in javascript在javascript中将文件/目录结构转换为“树”
【发布时间】:2013-10-23 01:58:03
【问题描述】:

我有一个如下所示的对象数组:

[{ name: 'test',
  size: 0,
  type: 'directory',
  path: '/storage/test' },
{ name: 'asdf',
  size: 170,
  type: 'directory',
  path: '/storage/test/asdf' },
{ name: '2.txt',
  size: 0,
  type: 'file',
  path: '/storage/test/asdf/2.txt' }]

可能有任意数量的任意路径,这是遍历目录中的文件和文件夹的结果。

我要做的是确定这些的“根”节点。最终,这将存储在 mongodb 中并使用物化路径来确定它的关系。

在此示例中,/storage/test 是没有父级的根。 /storage/test/asdf 具有 /storage/test 的父级,它是 /storage/test/asdf/2.txt 的父级。

我的问题是,您将如何遍历此数组以确定父级和关联的子级?在正确方向上的任何帮助都会很棒!

谢谢

【问题讨论】:

  • 您是否正在寻找能够真正为您提供类似目录树结构的东西,其中/storage/test 属于隐含节点/storage,而隐含节点/ 又属于隐含根/?不完全确定您最终要使用哪种数据结构。
  • 为了澄清我的评论,如果您在/storage/test 有一个文件,在/storage/text/asdf/2.txt 有一个文件,那么必须隐含/storage/text/asdf,除非父母也可能意味着祖父母。
  • 好吧,我想基本上有嵌套的孩子,所以一个 json 对象表示与“文件夹树”相同,如果这有意义的话。我想我必须递归地遍历它才能保存。

标签: javascript node.js tree


【解决方案1】:

你可以这样做:

var arr = [] //your array;
var tree = {};

function addnode(obj){
  var splitpath = obj.path.replace(/^\/|\/$/g, "").split('/');
  var ptr = tree;
  for (i=0;i<splitpath.length;i++)
  {
    node = { name: splitpath[i],
    type: 'directory'};
    if(i == splitpath.length-1)
    {node.size = obj.size;node.type = obj.type;}
    ptr[splitpath[i]] = ptr[splitpath[i]]||node;
    ptr[splitpath[i]].children=ptr[splitpath[i]].children||{};
    ptr=ptr[splitpath[i]].children;
  }    
}

arr.map(addnode);
console.log(require('util').inspect(tree, {depth:null}));

输出

{ storage:
   { name: 'storage',
     type: 'directory',
     children:
      { test:
         { name: 'test',
           type: 'directory',
           size: 0,
           children:
            { asdf:
               { name: 'asdf',
                 type: 'directory',
                 size: 170,
                 children: { '2.txt': { name: '2.txt', type: 'file', size: 0, children: {} } } } } } } } }

【讨论】:

    【解决方案2】:

    假设/ 永远不会出现在文件列表中,这样的事情应该可以工作:

    function treeify(files) {
      var path = require('path')
    
      files = files.reduce(function(tree, f) {
        var dir = path.dirname(f.path)
    
        if (tree[dir]) {
          tree[dir].children.push(f)
        } else {
          tree[dir] = { implied: true, children: [f] }
        }
    
        if (tree[f.path]) {
          f.children = tree[f.path].children
        } else {
          f.children = []
        }
    
        return (tree[f.path] = f), tree
      }, {})
    
      return Object.keys(files).reduce(function(tree, f) {
        if (files[f].implied) {
          return tree.concat(files[f].children)
        }
    
        return tree
      }, [])
    }
    

    它会把你在问题中提到的数组变成这样的:

    [ { name: 'test',
        size: 0,
        type: 'directory',
        path: '/storage/test',
        children: 
         [ { name: 'asdf',
             size: 170,
             type: 'directory',
             path: '/storage/test/asdf',
             children: 
              [ { name: '2.txt',
                  size: 0,
                  type: 'file',
                  path: '/storage/test/asdf/2.txt',
                  children: [] } ] } ] } ]
    

    我实际上并没有使用任何其他数据源对此进行过测试,因此您的里程可能会有所不同,但至少它应该可以将您推向正确的方向。

    【讨论】:

    • 这行得通,但你能在你的代码中添加一些 cmets,我在理解所有这些方面遇到了问题。具体来说,在第一个 reduce 之后,除了所有其他对象之外,还有一个隐含对象,然后您将使用第二个 reduce 丢弃所有其他对象。不能一开始就做吗?
    • 缺少一级目录“storage”。
    【解决方案3】:

    基于@user568109但以数组而不是对象返回结果的解决方案:

    function filesToTreeNodes(arr) {
      var tree = {}
      function addnode(obj) {
        var splitpath = obj.fileName.replace(/^\/|\/$/g, "").split('/');
        var ptr = tree;
        for (let i = 0; i < splitpath.length; i++) {
          let node: any = {
            fileName: splitpath[i],
            isDirectory: true
          };
          if (i == splitpath.length - 1) {
            node.isDirectory = false
          }
          ptr[splitpath[i]] = ptr[splitpath[i]] || node;
          ptr[splitpath[i]].children = ptr[splitpath[i]].children || {};
          ptr = ptr[splitpath[i]].children;
        }
      }
      function objectToArr(node) {
        Object.keys(node || {}).map((k) => {
          if (node[k].children) {
            objectToArr(node[k])
          }
        })
        if (node.children) {
          node.children = Object.values(node.children)
          node.children.forEach(objectToArr)
        }
      }
      arr.map(addnode);
      objectToArr(tree)
      return Object.values(tree)
    }
    

    这是更好地理解输入/输出格式的签名:

    export interface TreeNode {
      isDirectory: string
      children: TreeNode[]
      fileName: string
    }
    export interface File {
      fileName: string
    }
    export type fileToTreeNodeType = (files: File[]) => TreeNode[]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多