1

I have the following code to list all folders within a directory, and it maps each folder's name and date created. I'm struggling to get the last modified file within the sub directories of the folders.

const fs = require('fs')
const glob = require("glob")

const getDirectories = (source, callback) => {

    const getDirectories = function (src, callback) {
        glob(src + '/*/*/*', callback)
    }

    fs.readdir(source, { withFileTypes: true }, (err, files) => {
        if (err) {
            callback(err)
        } 
        else {
            callback(
                files
                .filter(dirent => dirent.isDirectory())
                .map(name => ({
                    name: name.name,
                    created: fs.statSync(`${source}\\${name.name}`).birthtime.toLocaleString(navigator.language, {
                            day: '2-digit',
                            month: 'short',
                            year: '2-digit',
                            hour: '2-digit',
                            minute: '2-digit'
                        }
                    ),
                    modified: getDirectories(`${source}\\${name.name}`, function (err, res) {
                        res = res.map(file => ({ file, mtime: fs.lstatSync(file).mtime }))
                        .sort((a, b) => b.mtime - a.mtime)
                        return res.length ? res[0] : ''
                    })
                }))
                .sort(function (a, b) {
                    if ($sort == 1) {
                        if (a.name < b.name) return -1;
                        if (a.name > b.name) return 1; 
                        return 0;
                    }
                    else if ($sort == 2) {
                        if (a.name > b.name) return -1;
                        if (a.name < b.name) return 1; 
                        return 0;
                    }
                    else if ($sort == 3) {
                        if (a.created < b.created) return -1;
                        if (a.created > b.created) return 1; 
                        return 0;
                    }
                    else if ($sort == 4) {
                        if (a.created > b.created) return -1;
                        if (a.created < b.created) return 1; 
                        return 0;
                    }
                    else if ($sort == 5) {
                        if (a.modified < b.modified) return -1;
                        if (a.modified > b.modified) return 1; 
                        return 0;
                    }
                    else if ($sort == 6) {
                        if (a.modified > b.modified) return -1;
                        if (a.modified < b.modified) return 1; 
                        return 0;
                    }
                })
            )
        }
    })
}

My problem is with the follwing snippet:

modified: getDirectories(`${source}\\${name.name}`, function (err, res) {
                    res = res.map(file => ({ file, mtime: fs.lstatSync(file).mtime }))
                    .sort((a, b) => b.mtime - a.mtime)
                    return res.length ? res[0] : ''
                })

I'm struggling to get a return value to set the modified object. If I do console.log(res) I can see that I get the expected result

I'm listing all the folders in HTML with the name, created and modified properties.

An example of the directories:

Working Dir
├──Work Space1
   └──Category1
      └──Location_a
         └── *.file_a1
         └── *.file_a2
         └── *.file_a3
      └──Location_b
         └── *.file_b1
         └── *.file_b2
         └── *.file_b3
   └──Category2
      └──Location_a
         └── *.file_a1
         └── *.file_a2
         └── *.file_a3
      └──Location_b
         └── *.file_b1
         └── *.file_b2
         └── *.file_b3
├──Work Space2
   └──Category1
      └──Location_a
         └── *.file_a1
         └── *.file_a2
         └── *.file_a3
      └──Location_b
         └── *.file_b1
         └── *.file_b2
         └── *.file_b3
   └──Category2
      └──Location_a
         └── *.file_a1
         └── *.file_a2
         └── *.file_a3
      └──Location_b
         └── *.file_b1
         └── *.file_b2
         └── *.file_b3

I need to get the last modified file of each file within each Work Space regardless of the sub folders

And example of the HTML output:

enter image description here

DogFoxX
  • 111
  • 10

2 Answers2

1

try with recursive function, add stats you need, then you can sort the results.

check the code (directory path is included so you could work out hierarchy if needed (credits for reading files solution: Smally's answer)):

const getDirectories = (source) => {

    const files = [];

    function getFiles(dir) {

        fs.readdirSync(dir).map(file => {

            const absolutePath = path.join(dir, file);

            const stats = fs.statSync(absolutePath);

            if (fs.statSync(absolutePath).isDirectory()) {

                return getFiles(absolutePath);

            } else {
                const modified = {
                    name: file,
                    dir: dir,
                    created: stats.birthtime.toLocaleString(navigator.language),
                    modified: stats.mtime
                };
                return files.push(modified);
            }
        });
    }

    getFiles(source);
    return files;
}


const files = getDirectories(somePath);

console.log('\ngot files:', files.length);

// sort...
const lastModified = files.sort((a, b) => b.modified - a.modified);

console.log('\nlast modified:\n', lastModified[0]);
traynor
  • 5,490
  • 3
  • 13
  • 23
  • I have tried Sally's answer before and couldn't get it to work. Thank you for your answer though, it works! Just one other thing, how can I return `files` but ignore the first one. The reason is that the first file will change on interaction with elements, but the other nested files hold data. – DogFoxX Jan 28 '22 at 17:58
  • I forgot to mention in my question within each `Work Space` root there is a file, and I want to ignore that one. – DogFoxX Jan 28 '22 at 18:17
  • Got it working with `getFiles(source); return files.slice(1);` – DogFoxX Jan 28 '22 at 19:04
  • great. so it's literally a single file.. I was about to suggest that you could sort it, but then I thought maybe you want to ignore many files, so having an exclusion list would be nice, but it's quite ugly with fs, so going back to glob would be more appropriate.. – traynor Jan 28 '22 at 19:20
1

Thanks to traynor's answer I got it working. Had to make some changes to make it work for my use case.

Full code:

const getDirectories = (source, callback) => {

    const getAllFiles = (dir) => {
        let files = [];

        const getFiles = (dir) => {
            fs.readdirSync(dir).map(file => {
                const absolutePath = path.join(dir, file)
                const stats = fs.statSync(absolutePath)
                
                if (fs.statSync(absolutePath).isDirectory()) {
                    return getFiles(absolutePath)
                }
                else {
                    let modified = stats.mtime
                    
                    return files.push(modified)
                }
            })
        }

        getFiles(dir)
        return files.slice(1)
    }

    const getLastModified = (dir) => {
        let files = getAllFiles(dir)

        files = files.sort((a, b) => b - a)
        return files[0]
    }

    fs.readdir(source, { withFileTypes: true }, (err, files) => {
        if (err) {
            callback(err)
        } 
        else {
            callback(
                files
                .filter(dirent => dirent.isDirectory())
                .map(name => ({
                    name: name.name,
                    created: fs.statSync(`${source}\\${name.name}`).birthtime.toLocaleString(navigator.language, {
                            day: '2-digit',
                            month: 'short',
                            year: '2-digit',
                            hour: '2-digit',
                            minute: '2-digit'
                        }
                    ),
                    modified: getLastModified(`${source}\\${name.name}`).toLocaleString(navigator.language, {
                            day: '2-digit',
                            month: 'short',
                            year: '2-digit',
                            hour: '2-digit',
                            minute: '2-digit'
                        }
                    )
                }))
                .sort(function (a, b) {
                    if ($sort == 1) {
                        if (a.name < b.name) return -1;
                        if (a.name > b.name) return 1; 
                        return 0;
                    }
                    else if ($sort == 2) {
                        if (a.name > b.name) return -1;
                        if (a.name < b.name) return 1; 
                        return 0;
                    }
                    else if ($sort == 3) {
                        if (a.created < b.created) return -1;
                        if (a.created > b.created) return 1; 
                        return 0;
                    }
                    else if ($sort == 4) {
                        if (a.created > b.created) return -1;
                        if (a.created < b.created) return 1; 
                        return 0;
                    }
                    else if ($sort == 5) {
                        if (a.modified < b.modified) return -1;
                        if (a.modified > b.modified) return 1; 
                        return 0;
                    }
                    else if ($sort == 6) {
                        if (a.modified > b.modified) return -1;
                        if (a.modified < b.modified) return 1; 
                        return 0;
                    }
                })
            )
        }
    })
}

Call it with:

let workSpaces

getDirectories(workSpaceDir, (dirs) => {
   workSpaces = dirs
})

This will return an array with objects name, created and modified.

As for the whole block of

.sort(function (a, b) {
    if ($sort == 1) {
       if (a.name < b.name) return -1;
       if (a.name > b.name) return 1; 
       return 0;
    }...
})

...I have a <select/> that sets $sort (witch is a svelete store) the id of the option selected. This will sort the workSpaces array accordingly.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
DogFoxX
  • 111
  • 10