简体   繁体   English

在javascript中将文件/目录结构转换为'tree'

[英]transform file/directory structure into 'tree' in javascript

I have an array of objects that looks like this: 我有一个对象数组,如下所示:

[{ 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' }]

There could be any number of arbitrary path's, this is the result of iterating through files and folders within a directory. 可以有任意数量的任意路径,这是迭代目录中的文件和文件夹的结果。

What I'm trying to do is determine the 'root' node of these. 我要做的是确定这些的“根”节点。 Ultimately, this will be stored in mongodb and use materialized path to determine it's relationships. 最终,这将存储在mongodb中并使用物化路径来确定它的关系。

In this example, /storage/test is a root with no parent. 在此示例中, /storage/test是没有父级的根。 /storage/test/asdf has the parent of /storage/test which is the parent to /storage/test/asdf/2.txt . /storage/test/asdf具有/storage/test的父级,它是/storage/test/asdf/2.txt的父级。

My question is, how would you go about iterating through this array, to determine the parent's and associated children? 我的问题是,你将如何迭代这个数组,以确定父母和相关的孩子? Any help in the right direction would be great! 任何正确方向的帮助都会很棒!

Thank you 谢谢

You can do it like this: 你可以这样做:

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

Output 产量

{ 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: {} } } } } } } } }

Assuming / will never show up in the list of files, something like this should work: 假设/永远不会出现在文件列表中,这样的东西应该有效:

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
  }, [])
}

It'll turn the array you mention in the question in to something like this: 它会将您在问题中提到的数组转换为以下内容:

[ { 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: [] } ] } ] } ]

I haven't actually tested this with any other data sources, so your milage may vary but at least it ought to nudge you in the right direction. 我实际上没有用任何其他数据源对此进行测试,所以你的milage可能会有所不同,但至少它应该在正确的方向上推动你。

Solution based on @user568109 but returning results in arrays instead of objects: 解决方案基于@ 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)
}

This is the signature to better understand the input / output formats: 这是更好地理解输入/输出格式的签名:

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

If you want instead to start from the root path, this is the best module for that task: 如果您希望从根路径开始,这是该任务的最佳模块:

npm dree npm dree

You can use several configurations and you can get a result similar to this: 您可以使用多种配置,您可以获得与此类似的结果:

{
  "name": "sample",
  "path": "D:/Github/dree/test/sample",
  "relativePath": ".",
  "type": "directory",
  "size": "1.79 MB",
  "children": [
    {
      "name": "backend",
      "path": "D:/Github/dree/test/sample/backend",
      "relativePath": "backend",
      "type": "directory",
      "size": "1.79 MB",
      "children": [
        {
          "name": "firebase.json",
          "path": "D:/Github/dree/test/sample/backend/firebase.json",
          "relativePath": "backend/firebase.json",
          "type": "file",
          "extension": "json",
          "size": "29 B"
        }, 
        {
          "name": "server",
          "path": "D:/Github/dree/test/sample/backend/server",
          "relativePath": "backend/server",
          "type": "directory",
          "size": "1.79 MB",
          "children": [
            {
              "name": "server.ts",
              "path": "D:/Github/dree/test/sample/backend/server/server.ts",
              "relativePath": "backend/server/server.ts",
              "type": "file",
              "extension": "ts",
              "size": "1.79 MB"
            }
          ]
        }
      ]
    }
  ]
}

Even a string could be returned, like this: 甚至可以返回一个字符串,如下所示:

sample
 └─> backend
     ├── firebase.json
     ├── hello.txt
     └─> server
         └── server.ts

For example: 例如:

const dree = require('dree');

const config = {
  normalize: true,
  stat: false,
  size: true,
  followLinks: true,
  exclude: [/exclude_me/, /exclude_me_too/ ],
  depth: 10,
  extensions: [ 'html', 'txt ]
};

const tree = dree.scan('./dir', config);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM