JavaScript Interview Question 1 ( Directory Mapping ๐Ÿ“‚ )

JavaScript Interview Question 1 ( Directory Mapping ๐Ÿ“‚ )

How to create a directory structure in HTML, based on the given directory object using JavaScript.

ยท

6 min read

Problem

We are given an object representing a directory structure, where each directory can contain files and subdirectories in a nested fashion. The object contains fields such as name, isFolder (to check if it is a file or folder), and children (an array representing the nested directories and files). The task is to create an HTML representation of the directory structure and append it to the body element.

const directory = {
            name: 'project',
            isFolder: true,
            children: [
                {
                    name: "public",
                    isFolder: true,
                    children: [
                        {
                            name: "index.html",
                            isFolder: false,
                        },
                        {
                            name: "favicon.ico",
                            isFolder: false,
                        },
                    ]
                },
                {
                    name: "src",
                    isFolder: true,
                    children: [
                        {
                            name: "index.css",
                            isFolder: false,
                        },
                        {
                            name: "index.js",
                            isFolder: false,
                        },
                        {
                            name: "scripts",
                            isFolder: true,
                            children: [
                                {
                                    name: "about.js",
                                    isFolder: false,
                                },
                                {
                                    name: "home.js",
                                    isFolder: false,
                                },
                            ]
                        }
                    ]
                }
            ]
        }

Representing the directory

We can represent the directory using the HTML <details> and <summary> elements allow us to create an expandable directory-like structure in the HTML representation. The <details> element serves as the container for each directory, and the <summary> element acts as the label or name of the directory. By utilizing these elements, we can achieve a collapsible and expandable directory structure in HTML.

<details>
   <summary>
      Click This to expand
   </summary>
   <p>
    The details of the clicked summary is visible. 
   </p>
</details>

Approach

First Create a function for mapping the directory and a helper function inside it to perform recursion for mapping the nested directory. The directoryMap function takes two arguments first the directory object and the DOM entry node of the HTML in which we have to put the directory mapping.

const directoryMap = (directory, entry) => {
       const helper = (directory) => {

       }

        const domElement = helper(directory);
       // if the helper function returns null skip appending
        if (domElement !== null) {
            entry.appendChild(domElement);
        }
}

const body = document.querySelector('body');
directoryMap(directory, body);

Now Let's complete our helper function, The helper function is responsible for mapping a directory object to its corresponding HTML representation. It takes a directory object as input and performs recursion to handle nested directories and files.

const helper = (directory) => {
    const { name, isFolder, children } = directory;
    if (isFolder) {
            var el = document.createElement('details');
        } else {
            var el = document.createElement('p');
     }
}

The helper function begins by extracting the name, isFolder, and children properties from the directory object. It then creates the appropriate HTML element based on whether it is a folder or a file. If it is a folder, it creates <details> element, otherwise, it creates <p> element, since there is no need to expand or a file doesn't have nesting.

const fname = document.createElement('summary');
fname.innerText = name;

Next, it creates <summary> element and sets its text content to the directory's name. The <summary> element serves as the label for the directory, then append the summary to the el which is the parent of the current directory.

If the directory is a folder, it proceeds to create <p> element to hold the nested directories and files. It iterates over the children array and recursively calls the helper function for each child directory or file. The resulting HTML elements are appended to a fragment, which is then appended to the <p> element.

Finally, the <p> element (containing nested directories/files, if applicable) is appended to the main element (<details> or <p>) representing the current directory. The main element is then returned.

const helper = (directory) => {
    const { name, isFolder, children } = directory;
    if (isFolder) {
        var el = document.createElement('details');
    } else {
        var el = document.createElement('p');
    }

    const fname = document.createElement('summary');
     // applying file/folder name in summary element
    fname.innerText = name;
    // adding summary element to <details>/<p>
    el.appendChild(fname);
    // creating p element for holding directory children
    const desc = document.createElement('p');

    /* if the directory is a folder the iterate over its children 
    * array and recursively calls the helper function for 
    * each child directory or file. 
    */
    if (isFolder) {
        // Fragment is used here for performance optimization.
        for (let child of children) {
            fragment.appendChild(helper(child));
        }
        desc.appendChild(fragment);
    }

    el.appendChild(desc);
    return el;
}

The benefit of creating a document fragment and appending child elements to it before appending them to the main element (<details> or <p>) lies in performance optimization since we aren't manipulating dom for each child and its nesting, we are putting all the nesting of a child in a nesting and once done we are putting the nesting in <p> element.

/**
* @param {Object} directory 
* @param {HTMLElement} entry 
 */
const directoryMap = (directory, entry) => {
      const helper = (directory) => {
          const { name, isFolder, children } = directory;
          // if the directory is folder we create <summary> element else <p>
          if (isFolder) {
               var el = document.createElement('details');
           } else {
               var el = document.createElement('p');
           }

            const fname = document.createElement('summary');
             // applying file/folder name in summary element
            fname.innerText = name;
            // adding summary element to <details>/<p>
            el.appendChild(fname);
            // creating p element for holding directory children
            const desc = document.createElement('p');

            /* if the directory is a folder the iterate over its children 
            * array and recursively calls the helper function for 
            * each child directory or file. 
            */
            if (isFolder) {
                // Fragment is used here for performance optimization.
                for (let child of children) {
                    fragment.appendChild(helper(child));
                }
                desc.appendChild(fragment);
            }
            // adding the children to the respective parent directory
            el.appendChild(desc);
            // returning the current directory node
            return el;
        }
        // getting the directory dom structure
        const domElement = helper(directory);
        // putting the directory dom structure in entry elemrnt if it is not null.
        if (domElement !== null) {
             entry.appendChild(domElement);
        }
}

Back in the directoryMap function, the helper function is called with the directory object and the resulting HTML element is stored in domElement. If domElement is not null, it means there is a valid HTML representation of the directory, so it is appended to the entry element (DOM node) provided as an argument.

By executing directoryMap(directory, body), the directory mapping is generated and appended to the <body> element of the HTML document.

Full Code

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Directory Mapping</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        p {
            margin-left: 10px;
        }

        details {
            padding: 3px;
        }
    </style>
</head>

<body>
    <script>
        const directory = {
            name: 'project',
            isFolder: true,
            children: [
                {
                    name: "public",
                    isFolder: true,
                    children: [
                        {
                            name: "index.html",
                            isFolder: false,
                        },
                        {
                            name: "favicon.ico",
                            isFolder: false,
                        },
                    ]
                },
                {
                    name: "src",
                    isFolder: true,
                    children: [
                        {
                            name: "index.css",
                            isFolder: false,
                        },
                        {
                            name: "index.js",
                            isFolder: false,
                        },
                        {
                            name: "scripts",
                            isFolder: true,
                            children: [
                                {
                                    name: "about.js",
                                    isFolder: false,
                                },
                                {
                                    name: "home.js",
                                    isFolder: false,
                                },
                            ]
                        }
                    ]
                }
            ]
        }

        /**
         * 
         * @param {Object} directory 
         * @param {HTMLElement} entry 
         */
        const directoryMap = (directory, entry) => {
            const helper = (directory) => {
                const { name, isFolder, children } = directory;
                if (isFolder) {
                    var el = document.createElement('details');
                } else {
                    var el = document.createElement('p');
                }

                const fname = document.createElement('summary');
                fname.innerText = name;
                el.appendChild(fname);
                const desc = document.createElement('p');

                if (isFolder) {
                    const fragment = document.createDocumentFragment();
                    for (let child of children) {
                        fragment.appendChild(helper(child));
                    }
                    desc.appendChild(fragment);
                }

                el.appendChild(desc);
                return el;
            }

            const domElement = helper(directory);
            if (domElement !== null) {
                entry.appendChild(domElement);
            }
        }

        const body = document.querySelector('body');
        directoryMap(directory, body);
    </script>
</body>

</html>

Output

Conclusion

In this blog post, we explored a common JavaScript interview question related to directory mapping. We discussed a method for creating an HTML representation of a directory structure using <details> and <summary> elements, which allows for an extendible and collapsible directory structure-like structure.


If you enjoyed this article, please leave a like. I highly appreciate any feedback from you and any suggestions are welcome too!

Follow me on Twitter.

ย