Search code examples
javascriptreactjstypescriptwebnext.js

Converting "/" sperated data into a tree like structure


Everyone I'm trying to build something like google drive. I have finished the file upload functionality and I'm now working on the folder upload functionality for which I'm using the HTML file input (I'm using Next.js/React).

 <input
        type="file"
        onChange={(e) => {
          setFiles(e.target.files);
        }}
        webkitdirectory="true"
        directory="true"
        multiple
      />

after selecting a folder and uploading it I'm getting a File list where I have the webkitRelativePath property for each file like this --> "folder/childFolder/file".

now let's say I uploaded a folder which has many files and folders inside it add these are the webkitRelativePath property for all the files

[ 'folder/childFile', 'folder/childFile2', 'folder/childFile3', 'folder/childFolder/childFile4', 'folder/childFolder/childFile5', 'folder/childFolder/grandchildFolder/childFile6', 'folder/childFolder/grandChildFolder/childFile7' ]

using this data I want to create a tree like structure like this -->

{
    name: "folder",
    children: [
      {
        name: "childFile",
        children: [],
      },
      {
        name: "childFile2",
        children: [],
      },
      {
        name: "childFile3",
        children: [],
      },
      {
        name: "childFolder",
        children: [
          {
            name: "childFile4",
            children: [],
          },
          {
            name: "childFile5",
            children: [],
          },
          {
            name: "grandchildFolder",
            children: [
              {
                name: "childFile6",
                children: [],
              },
              {
                name: "childFile7",
                children: [],
              },
            ],
          },
        ],
      },
    ],
  }

I tried using loops and recursion but I was not able to make it work because everyfile is prefixed with the root folder and many more issues


Solution

  • Main part is addPath function that can work both with empty object and with existing tree.

    const tree = {};
    
    const list = [
      "folder/childFile",
      "folder/childFile2",
      "folder/childFile3",
      "folder/childFolder/childFile4",
      "folder/childFolder/childFile5",
      "folder/childFolder/grandchildFolder/childFile6",
      "folder/childFolder/grandChildFolder/childFile7"
    ];
    
    const addPath = (path, tree) => {
      // helper function to create child objects
      const createChild = (name) => ({
        name,
        children: []
      });
    
      // split path to array of folders and files
      const parts = path.split("/");
    
      // create tree if empty
      if (!tree.name) {
        Object.assign(tree, createChild(parts[0]));
      }
    
      // check if root folder is correct
      if (tree.name !== parts[0]) {
        throw new Error(`Root folder is not "${tree.name}"`);
      }
      parts.shift();
    
      // check and add other path parts
      parts.reduce((current, p) => {
        const child = current.children.find((child) => child.name === p);
        if (child) {
          return child;
        }
    
        const newChild = createChild(p);
        current.children.push(newChild);
        return newChild;
      }, tree);
    };
    
    list.forEach((path) => addPath(path, tree));
    
    console.log(tree);