Search code examples
javascriptrecursiontraversal

I need to traverse an object and return some values from it recursively


I'm getting stuck when I pass the optional 2nd parameter to 'listFiles'. I'm struggling to find out how to list the files inside a given sub-directory. List the files in a given directory, of a file-system described by data. So, you should be able to return a list of files from the data.

Parameters:

data {Object} - a file system object as described above

dirName {String} - a directory name the files are desired to be listed from.

Note: This parameter is optional. If it is not provided, list ALL files.

Returns {Array} The files under the directory dirName, including sub-directories.

The approach to complete this exercise should be using recursion. I was able to return all the files from the file system object with the code below:

const fileData = {
  dirName: 'app',
  files: ['index.html'],
  subDirs: [{
      dirName: 'js',
      files: [
        'main.js',
        'app.js',
        'misc.js',
      ],
      subDirs: [{
        dirName: 'vendor',
        files: [
          'jquery.js',
          'underscore.js',
        ],
        subDirs: [],
      }, ],
    },
    {
      dirName: 'css',
      files: [
        'reset.css',
        'main.css',
      ],
      subDirs: [],
    },
  ],
};

function listFiles(data, dirName) {

    let result = [];

    const traverseFileSystem = (obj) => {
      Object.keys(obj).forEach((key) => {
        if (obj[key] && typeof obj[key] === 'object') {
          if (key === 'files') {
            result = [...result, ...obj[key]];
          }
          traverseFileSystem(obj[key]);
        }
      });
    };

    traverseFileSystem(data);

    console.log(result);
  }

listFiles(fileData, 'js');


Solution

  • try

    const trFS= dir=> dir.dirName==dirName ? dir : dir.subDirs.flatMap(d=> trFS(d));
    
    const trFSNoDir = dir => [...dir.subDirs.flatMap(d=> trFSNoDir(d)), ...dir.files];
    

    const fileData = {
      dirName: 'app',
      files: ['index.html'],
      subDirs: [{
          dirName: 'js',
          files: [
            'main.js',
            'app.js',
            'misc.js',
          ],
          subDirs: [{
            dirName: 'vendor',
            files: [
              'jquery.js',
              'underscore.js',
            ],
            subDirs: [],
          }, ],
        },
        {
          dirName: 'css',
          files: [
            'reset.css',
            'main.css',
          ],
          subDirs: [],
        },
      ],
    };
    
    function listFiles(data, dirName) {
    
        const trFS= dir=> dir.dirName==dirName ? dir : dir.subDirs.flatMap(d=> trFS(d));
        
        const trFSNoDir = dir => [...dir.subDirs.flatMap(d=> trFSNoDir(d)), ...dir.files];
        
        return dirName ? trFS(data)[0] : trFSNoDir(data);        
    }
    
    console.log("dir name: js\n", listFiles(fileData, 'js'));
    console.log("dir name: css\n", listFiles(fileData, 'css'));
    console.log("dir name: -\n",listFiles(fileData));

    When dirName is given I return object (instead of array) with files and subdirectories because you not specified how output array should looks like (e.g. does subdirectories in that array should be objects or names only) - this object can be mapped in easy way to array (e.g. if r contains result then by [...r.files,...r.subDirs]).