У меня есть каталог с множеством подкаталогов, которые, в свою очередь, имеют несколько подкаталогов. Подумайте о строках vvv example1 plugin1 plugin2 example2 plugin1 plugin2 ...

0
HJW 19 Янв 2021 в 02:08

1 ответ

Лучший ответ

Вы добавляете каждую запись как уникальную, а не фильтруете существующие.

Примечание. Как и в исходном вопросе, в следующих примерах отсутствует обработка ошибок. Неасинхронные примеры (все, кроме последнего) потребуют блоков try {} catch(err) {}. Примеры async также могут использовать их или Promise.prototype.catch в цепочке после блоков then.

Вместо этого вы можете использовать объект

    const main = fs.readdirSync("./vvv");
    const newArr = {}; // Object, not array

    main.forEach((dir) => {
      const plugins = fs.readdirSync(`./vvv/${dir}`);

      plugins.forEach((plugin, index) => {
        if(plugin in newArr) {
          newArr[plugin]++;
        }
        else {
          newArr[plugin] = 1;
        }
      });
    });

Или используйте Array.find

    const main = fs.readdirSync("./vvv");
    const newArr = [];

    main.forEach((dir) => {
      const plugins = fs.readdirSync(`./vvv/${dir}`);

      plugins.forEach((plugin, index) => {
        const found = newArr.find(p => p.name === plugin);
        if(found) {
          found.count++;
        }
        else {
          newArr.push({ name: plugin, count: 1 });
        }
      });
    });

Вы можете использовать Array.reduce

    const main = fs.readdirSync("./vvv");
    const newArr = main.reduce((acc, dir) => {
      const plugins = fs.readdirSync(`./vvv/${dir}`);

      plugins.forEach((plugin, index) => {
        const found = acc.find(p => p.name === plugin);
        if(found) {
          found.count++;
        }
        else {
          acc.push({ name: plugin, count: 1 });
        }
      });

      return acc;
    }, []);

Вы можете использовать двойной Array.reduce и объект, неизменяемый

    const main = fs.readdirSync("./vvv");
    const newArr = main.reduce((acc, dir) => (
      fs.readdirSync(`./vvv/${dir}`)
        .reduce((accInner, plugin) => ({
           // Return all already collected plugins
           ...accInner,
           // For the currently handled plugin...
           [plugin]: (plugin in accInner)
             // If already in othe object, return its value + 1
             ? accInner[plugin] + 1
             // If not, create it with a 1 counter
             : 1,
         }), acc) // Initialize accInner to acc
    ), {}); // Object, not array

Вы можете использовать двойные Array.reduce и Array.find, неизменяемые

    const main = fs.readdirSync("./vvv");
    const newArr = main.reduce((acc, dir) => (
      fs.readdirSync(`./vvv/${dir}`)
        .reduce((accInner, plugin) => (
           accInner.find(p => p.name === plugin)
             // If a plugin with that name is found in inner accumulator,
             // return a map of that accumulator, modifiying the current
             // plugin (count+1) and passing through the rest
             ? accInner.map(p =>
                 p.name === plugin
                   ? {...p, count: p.count + 1}
                   : p
               )
             // If none is found, return a new array with all the 
             // collected plugins, plus the new one
             : [...accInner, {name: plugin, count: 1}]
         ), acc) // Initialize accInner to acc
    ), []); // Array

Вы можете использовать двойные Array.reduce и Array.find, неизменяемые, используя async / await и асинхронную версию на fs

В этом примере рабочий процесс должен:

  • Запускаем и ждем разрешения readdir main: мы ничего не можем сделать, пока не получим результаты
  • Запустить все readdir одновременно / параллельно. Здесь начинают свою работу все операции ввода-вывода.
  • Используйте метод reduce для последовательного выполнения обещаний. Ожидает выполнения первого обещания main элемента массива readdir, чтобы начать анализ всех результатов обещаний, некоторые из которых могут потребовать дополнительного ожидания.
  • Используйте reduce для вычисления значений, как в предыдущем примере.
var http = require("http");
var fs = require("fs").promises;

// You can only run async code (using `await` keyword) inside an async function
// You can use use `await` without an extra function declaring your package as a module in `package.json`

const getPluginsUsage = async () => {
  const main = await fs.readdir("./vvv");
  // Wait for all the `then` chain to fulfill
  const newArr = await main
    // Generate an array of promises from the `main` array of dirs
    // All IO operations start here, concurrently/in parallel
    .map( dir => fs.readdir(`./vvv/${dir}`))
    .reduce((prev, promise) => ( 
      // Once initial/previous promise fulfills,
      // add current promise to the `then` chain.
      //  - Get `acc` from previous promise resolution
      //  - Get `plugins` from current promise resolution

      // Note: Async functions return a promise
      //       resolving to its return value
      // This block could also be written as:
      // async (prev, promise) => {
      //   const acc = await prev;
      //   const plugins = await promise;
      //   return plugins.reduce(...);
      // }

      // Or:
      // prev
      //   .then( acc => promise.then( plugins => [acc, plugins] ))
      //   .then( ([acc, plugin]) => plugins.reduce(..) ) 

      // Or:
      // async (prev, promise) => {
      //   const [acc, plugins] = await Promise.all([prev, promise]);
      //   return plugins.reduce(...);
      // }

      prev.then( acc => promise.then( plugins => ( 
        // Parse results as in the previous example
        plugins.reduce( (accInner, plugin) => ( 
          acc.find(p => p.name === plugin)
            ? accInner.map(p =>
                p.name === plugin
                  ? {...p, count: p.count + 1}
                  : p 
              ) 
            : [...accInner, {name: plugin, count: 1}]
        ), acc)
      )))

    // Initial reduce value is a Promise that will resolve
    // to our accumulator base (here, an empty Array)
    ), Promise.resolve([]));

  // Async functions always return a promise
  // This one will resolve to newArr value
  return newArr;
} 

// Using node module package will probably make it easier to handle the http server (this next part is not tested)
getPluginsUsage().then( newArr => {
  http
    .createServer(function (req, res) {    
      console.log({newArr});
      res.end();
    })
    .listen(3000, () => {
      console.log("Server is running at port 3000...");
    });
});

Если вы чувствуете себя диким, вы также можете попробовать {{X0 }}, WeakMap, Set и WeakSet .

Редактировать

Дальнейшие чтения JavaScript:

Общие концепции программирования:

1
emi 19 Янв 2021 в 18:40