0
const fs = require('fs').promises;

async function readMe(folder) {
    let result = [];
    try {
        const content = await fs.readdir(folder, { withFileTypes: true });
        for (let item of content) {
            if (item.isDirectory()) {
                await readMe(`${folder}/${item.name}`);
            } else {
                if (item.name === "sales.json") {
                    result.push(`${folder}/${item.name}`);
                }
            }
        }
    } catch (err) {
        console.log(err);
    }
    return result;
}

async function main() {
    const results = await readMe("stores");
    console.log(results);
}

main();

I am trying to list the path to the sales.json inside the directories and sub directories of 'stores' folder.I am confused on why the above code doesn't give the expected result. While changing to this line inside the if statement :

result.push(... await readMe(`${folder}/${item.name}`) )

Makes the code work. If I encounter any sales.json file in my directories and sub-directories I am handling the path in the else statement .Then why do I need to again push it inside the if statement as well?

7
  • 2
    "If I encounter any sales.json file in my directories and sub-directories I am handling the path in the else statement ." - no; you are adding a sales.json you might have found in the current directory at that point. But the descending into the sub-folders, that happens via the recursive call inside the if-branch. And the result of that call, needs to get added to your result array as well - otherwise, it will get lost at this point, and not passed by to the previous level of the recursion. Commented Apr 29, 2024 at 10:41
  • I am having a hard time trying to understand this.If sales.json file is found in the sub directory won't the if condition be false and the else statement handle the result? Commented Apr 29, 2024 at 10:45
  • Yes, put you are pushing to a different result array there - the one that you initialized as an empty array, right at the start of the method - let result = []. That is a different variable named result, than the one you have on the next higher "level" of call. If you read through the explanation in What is recursion and when should I use it?, things should become clearer, I think. Commented Apr 29, 2024 at 10:50
  • Can you just clarify me on this matter.After finding the sales.json file under some sub directory the recursive function is called and as it is a file the else part of the code is executed.The array is updated in the else block.And then what happens?Is the last return statement executed and it returns the array to the where the function was called? Commented Apr 29, 2024 at 12:11
  • 1
    "Is the last return statement executed and it returns the array to the where the function was called?" - yes. And then on that next "upper" level, the returned array from the lower level, needs to be added to the array on that upper level. If you don't do that - then you would have determined the data for a subfolder via the recursive call, but you would be throwing that result away, when it returns from this level of the recursion, to the one above it. Commented Apr 29, 2024 at 12:24

1 Answer 1

0

Avoid mixing concerns to promote code reusability. Write a simple ls that recursively yields all paths -

import { readdir } from "fs/promises"
import { join } from "path"

async function* ls(path = ".") {
  yield path
  for (const dirent of await readdir(path, { withFileTypes: true }))
    if (dirent.isDirectory())
      yield* ls(join(path, dirent.name))
    else
      yield join(path, dirent.name)
}

Derive find from ls -

async function* find(path = ".", query) {
  for await (const f of ls(path))
    if (query(f) !== false)
      yield f
}

Use find with Array.fromAsync to derive your result -

import { basename } from "path"

Array.fromAsync(
  find(".", f => basename(f) == "sales.json")
)
.then(console.log, console.error)

Generators can be paused and resumed. This means we can avoid doing unnecessary work in the event an answer can be determined early. If we only want the first sales.json file -

async function findSalesJson(path = ".") {
  for await (const f of find(path, f => basename(f) == "sales.json"))
    return f
  throw Error("sales.json not found")
}

findSalesJson(".")
  .then(path => console.log("found sales.json at", path))
  .catch(console.error)

For added explanation and other ways to leverage async generators, see this Q&A.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.