Как создать иерархическое дерево из массива строк Javascript - PullRequest
1 голос
/ 03 мая 2020

Я пытаюсь создать дерево иерархии файлов из массива строк, однако я не совсем правильно понимаю, и я знаю , что это ужасно неэффективно, но не уверен, как его улучшить.

Учитывая, что у меня есть:

const paths = [
  "test_data/new_directory/ok.txt",
  "test_data/reads_1.fq",
  "test_data/test_ref.fa",
  "test_data/dir2/dir3/dir4/dir5/file1.txt",
  "test_data/dir2/dir3/dir4/dir5/file2.txt",
  "test_data/new_directory/file2.txt",
  "other_dir/dir2/newfile.xls",
  "other_dir/sub_dir/file1.xls",
  "other_dir/sub_dir/file2.xls",
  "third_dir/first.xls"
];

Я хотел бы получить следующий объект:

{
  "/other_dir": {
    path: "/other_dir",
    type: "folder",
    isRoot: true,
    children: ["/other_dir/dir2"]
  },
  "/other_dir/dir2": {
    path: "/other_dir/dir2",
    type: "folder",
    children: ["/other_dir/dir2/newfile.xls"]
  },
  "/other_dir/dir2/newfile.xls": {
    path: "/other_dir/dir2/newfile.xls",
    type: "file",
    children: []
  },
  "/other_dir/sub_dir": {
    path: "/other_dir/sub_dir",
    type: "folder",
    children: ["/other_dir/sub_dir/file1.xls", "/other_dir/sub_dir/file2.xls"]
  },
  "/other_dir/sub_dir/file1.xls": {
    path: "/other_dir/sub_dir/file1.xls",
    type: "file",
    children: []
  },
  "/other_dir/sub_dir/file2.xls": {
    path: "/other_dir/sub_dir/file2.xls",
    type: "file",
    children: []
  },
  "/test_data": {
    path: "/test_data",
    type: "folder",
    isRoot: true,
    children: [
      "/test_data/dir2",
      "/test_data/new_directory",
      "/test_data/test_ref.fa",
      "/test_data/reads_1.fq"
    ]
  },
  "/test_data/dir2": {
    path: "/test_data/dir2",
    type: "folder",
    children: ["/test_data/dir2/dir3", "/test_data/file2.txt"]
  },
  "/test_data/file2.txt": {
    path: "/test_data/file2.txt",
    type: "file",
    children: []
  },
  "/test_data/dir2/dir3": {
    path: "/test_data/dir2/dir3",
    type: "folder",
    children: ["/test_data/dir2/dir3/dir4"]
  },
  "/test_data/dir2/dir3/dir4": {
    path: "/test_data/dir2/dir3/dir4",
    type: "folder",
    children: ["/test_data/dir2/dir3/dir4/dir5"]
  },
  "/test_data/dir2/dir3/dir4/dir5": {
    path: "/test_data/dir2/dir3/dir4/dir5",
    type: "folder",
    children: [
      "/test_data/dir2/dir3/dir4/dir5/file1.txt",
      "/test_data/dir2/dir3/dir4/dir5/file2.txt"
    ]
  },
  "/test_data/dir2/dir3/dir4/dir5/file1.txt": {
    path: "/test_data/dir2/dir3/dir4/dir5/file1.txt",
    type: "file",
    children: []
  },
  "/test_data/dir2/dir3/dir4/dir5/file2.txt": {
    path: "/test_data/dir2/dir3/dir4/dir5/file2.txt",
    type: "file",
    children: []
  },
  "/test_data/new_directory": {
    path: "/test_data/new_directory",
    type: "folder",
    children: [
      "/test_data/new_directory/ok.txt",
      "/test_data/new_directory/file2.txt"
    ]
  },
  "/test_data/new_directory/file2.txt": {
    path: "/test_data/new_directory/file2.txt",
    type: "file",
    children: []
  },
  "/test_data/new_directory/ok.txt": {
    path: "/test_data/new_directory/ok.txt",
    type: "file",
    children: []
  },
  "/test_data/reads_1.fq": {
    path: "/test_data/reads_1.fq",
    type: "file",
    children: []
  },
  "/test_data/test_ref.fa": {
    path: "/test_data/test_ref.fa",
    type: "file",
    children: []
  },
  "/third_dir": {
    path: "/third_dir",
    type: "folder",
    isRoot: true,
    children: ["/third_dir/first.xls"]
  },
  "/third_dir/first.xls": {
    path: "/third_dir/first.xls",
    type: "file",
    children: []
  }
};

Это моя попытка, и я потратил слишком долго для этого

const buildChildNodes = (arr, root) => {
  let a = []

  arr.map((n, idx) => {
    a.push('/' + root + '/' + arr[idx])
  });
  return a;
}

const createTree = paths => {
  let finalTree = {};


  paths.map(path => {
    let tree = {};

    let subTree = path.split('/')
    subTree.forEach((a,i) => {
      tree = {}
      let root = subTree[0]

      tree.path = '/' + subTree.slice(0, i+1).join('/')
      tree.type = subTree.slice(i+1).length > 0 ? 'folder' : 'file'
      if(i === 0){
        tree.isRoot = true
      }
      tree.children = buildChildNodes(subTree.slice(i+1), root)

      finalTree['/' + subTree.slice(0, i+1).join('/')] = tree
    })



  })
  return finalTree;
};
console.log(JSON.stringify(createTree(paths.sort()), null, 2));

И вот, как вы можете видеть, дочерние узлы создаются неправильно:

{
  "/other_dir": {
    "path": "/other_dir",
    "type": "folder",
    "isRoot": true,
    "children": [
      "/other_dir/sub_dir",
      "/other_dir/file2.xls"
    ]
  },
  "/other_dir/dir2": {
    "path": "/other_dir/dir2",
    "type": "folder",
    "children": [
      "/other_dir/newfile.xls"
    ]
  },
  "/other_dir/dir2/newfile.xls": {
    "path": "/other_dir/dir2/newfile.xls",
    "type": "file",
    "children": []
  },
  "/other_dir/sub_dir": {
    "path": "/other_dir/sub_dir",
    "type": "folder",
    "children": [
      "/other_dir/file2.xls"
    ]
  },
  "/other_dir/sub_dir/file1.xls": {
    "path": "/other_dir/sub_dir/file1.xls",
    "type": "file",
    "children": []
  },
  "/other_dir/sub_dir/file2.xls": {
    "path": "/other_dir/sub_dir/file2.xls",
    "type": "file",
    "children": []
  },
  "/test_data": {
    "path": "/test_data",
    "type": "folder",
    "isRoot": true,
    "children": [
      "/test_data/test_ref.fa"
    ]
  },
  "/test_data/dir2": {
    "path": "/test_data/dir2",
    "type": "folder",
    "children": [
      "/test_data/dir3",
      "/test_data/dir4",
      "/test_data/dir5",
      "/test_data/file2.txt"
    ]
  },
  "/test_data/dir2/dir3": {
    "path": "/test_data/dir2/dir3",
    "type": "folder",
    "children": [
      "/test_data/dir4",
      "/test_data/dir5",
      "/test_data/file2.txt"
    ]
  },
  "/test_data/dir2/dir3/dir4": {
    "path": "/test_data/dir2/dir3/dir4",
    "type": "folder",
    "children": [
      "/test_data/dir5",
      "/test_data/file2.txt"
    ]
  },
  "/test_data/dir2/dir3/dir4/dir5": {
    "path": "/test_data/dir2/dir3/dir4/dir5",
    "type": "folder",
    "children": [
      "/test_data/file2.txt"
    ]
  },
  "/test_data/dir2/dir3/dir4/dir5/file1.txt": {
    "path": "/test_data/dir2/dir3/dir4/dir5/file1.txt",
    "type": "file",
    "children": []
  },
  "/test_data/dir2/dir3/dir4/dir5/file2.txt": {
    "path": "/test_data/dir2/dir3/dir4/dir5/file2.txt",
    "type": "file",
    "children": []
  },
  "/test_data/new_directory": {
    "path": "/test_data/new_directory",
    "type": "folder",
    "children": [
      "/test_data/ok.txt"
    ]
  },
  "/test_data/new_directory/file2.txt": {
    "path": "/test_data/new_directory/file2.txt",
    "type": "file",
    "children": []
  },
  "/test_data/new_directory/ok.txt": {
    "path": "/test_data/new_directory/ok.txt",
    "type": "file",
    "children": []
  },
  "/test_data/reads_1.fq": {
    "path": "/test_data/reads_1.fq",
    "type": "file",
    "children": []
  },
  "/test_data/test_ref.fa": {
    "path": "/test_data/test_ref.fa",
    "type": "file",
    "children": []
  },
  "/third_dir": {
    "path": "/third_dir",
    "type": "folder",
    "isRoot": true,
    "children": [
      "/third_dir/first.xls"
    ]
  },
  "/third_dir/first.xls": {
    "path": "/third_dir/first.xls",
    "type": "file",
    "children": []
  }
}

Если любой мог бы помочь мне в этом, я был бы очень признателен!

Спасибо!

Ответы [ 2 ]

2 голосов
/ 03 мая 2020

Вы можете получить вложенные части строк, проверить, завершается ли путь, или добавить новый путь. Затем возьмите предыдущий путь и добавьте детей.

const
    paths = ["test_data/new_directory/ok.txt", "test_data/reads_1.fq", "test_data/test_ref.fa", "test_data/dir2/dir3/dir4/dir5/file1.txt", "test_data/dir2/dir3/dir4/dir5/file2.txt", "test_data/new_directory/file2.txt", "other_dir/dir2/newfile.xls", "other_dir/sub_dir/file1.xls", "other_dir/sub_dir/file2.xls", "third_dir/first.xls"],
    result = paths.sort().reduce((r, p) => {
        p.split(/\//).forEach((_, i, p) => {
            var path = '/' + p.slice(0, i + 1).join('/');
            r[path] = r[path] || { path, type: i + 1 === p.length ? 'file' : 'folder', children: [] };
            if (i)  {
                const prev = '/' + p.slice(0, i).join('/');
                if (!r[prev].children.includes(path)) r[prev].children.push(path);
            } else {
                r[path].isRoot = true;
            }
        });
        return r;
    }, {});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
0 голосов
/ 03 мая 2020

Вы можете использовать два вложенных метода reduce, в которых вы добавляете путь к массиву дочерних элементов, только если есть следующая часть пути (что означает, что у текущих элементов есть дочерний элемент), а также если этот дочерний элемент уже не включен в массив.

const paths = [
  "test_data/new_directory/ok.txt",
  "test_data/reads_1.fq",
  "test_data/test_ref.fa",
  "test_data/dir2/dir3/dir4/dir5/file1.txt",
  "test_data/dir2/dir3/dir4/dir5/file2.txt",
  "test_data/new_directory/file2.txt",
  "other_dir/dir2/newfile.xls",
  "other_dir/sub_dir/file1.xls",
  "other_dir/sub_dir/file2.xls",
  "third_dir/first.xls"
];

const result = paths.reduce((acc, e) => {
  let prev = ''

  e.split('/').reduce((r, path, i, a) => {
    prev += '/' + path;

    if (!r[prev]) {
      const children = []
      const type = a[i + 1] ? 'folder' : 'file'
      const obj = { path: prev, type, children }

      if (i == 0) {
        obj.isRoot = true
      }

      r[prev] = obj
    }

    if (a[i + 1]) {
      const child = prev + '/' + a[i + 1];
      if (!r[prev].children.includes(child)) {
        r[prev].children.push(child)
      }
    }

    return r
  }, acc)

  return acc
}, {})

console.log(result)
...