javascript fs ищет файлы для строк в массиве. Избегайте плохой работы - PullRequest
0 голосов
/ 30 октября 2018

Я создаю инструмент, который будет очищать файл JSON, содержащий строки локализации, если они больше не используются в исходном коде.

Сначала я анализирую файл локализации в массив со всеми идентификаторами, которые используются (или больше не используются) в исходном коде, чтобы получить строковое значение на нужном языке.

поэтому у меня есть массив, который выглядит примерно так:

const ids = ['home.title', 'home.description', 'menu.contact', 'menu.social'];

и т.д.. Вы получаете точку.

Я использую файл обещаний node.js fs readFile и glob для поиска файлов с исходным кодом .js следующим образом:

const jsFiles = await globbing('./**/*.js', {cwd: directory, ignore: './**/*test.js'});
const results = jsFiles.map(async file => {
  const filePath = path.join(directory, file);
  return readFile(filePath, 'utf8').then((data) => {
      // handle match here
  }).catch(console.log);
});

У меня также есть Ramda для необычных функций списка / коллекции, но нет других библиотек.

Итак, я смогу перебрать массив ids и для каждого элемента отсканировать весь исходный код на совпадение с функцией сверху. Но это кажется немного излишним для сканирования всего исходного кода, умноженного на ids.length. Массив идентификаторов составляет около 400 идентификаторов, а исходный код состоит из сотен больших файлов.

Чтобы избежать O (M * N), есть ли способ сопоставить весь массив со всем исходным кодом и отбросить несоответствующие элементы массива? Или что будет лучшей практикой здесь?

текущее решение:

const cleanLocal = async () => {
  const localIdList = Object.keys(await getLocalMap());
  const matches = [];
  localIdList.map(async id => {
    const directory = path.join(__dirname, '..');
    const jsFiles = await globbing('./**/*.js', {cwd: directory, ignore: './**/*test.js'});
    jsFiles.map(async file => {
      const filePath = path.join(directory, file);
      return readFile(filePath, 'utf8').then((data) => {
        if (data.indexOf(id) >= 0) {
          console.log(id);
          matches.push(id);
        }
      }).catch(console.log);
    });
  });
};

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

Поскольку в этом случае невозможно избежать O (M * N), я смог оптимизировать эту функцию поиска только путем циклического обхода исходных файлов, а затем идентификаторов для каждого файла, как предложено @mihai как возможность оптимизации.

Конечный результат выглядит так:

const cleanLocal = async () => {
  const localIdList = Object.keys(await getLocalMap()); // ids' array
  const matches = [];
  const directory = path.join(__dirname, '..');
  const jsFiles = await globbing('./**/*.js', {cwd: directory, ignore: './**/*test.js'}); // list of files to scan
  const results = jsFiles.map(async file => {
    const filePath = path.join(directory, file);
    return readFile(filePath, 'utf8').then((data) => {
      localIdList.map(id => {
        if (R.contains(id, data)) { // R = ramda.js
          matches.push(id);
        }
      });
    }).catch(console.log);
  });
  await Promise.all(results);
  console.log('matches: ' + R.uniq(matches).length);
  console.log('in local.json: ' + localIdList.length);
};

Пожалуйста, дайте мне знать, если есть какой-либо другой способ оптимизировать это.

0 голосов
/ 30 октября 2018

Вы не можете избежать сложности O(M*N) в этом случае.

Однако, чтобы повысить производительность, вы можете переключать порядок своих операций: сначала цикл по файлам, а затем цикл по массиву. Это связано с тем, что зацикливание файлов является дорогостоящей операцией ввода-вывода, а зацикливание массива - операция с быстрой памятью.

В вашем коде у вас есть M операций с памятью и M*N операций ввода-вывода (файловая система).

Если вы сначала зациклите файлы, у вас будет N операций ввода-вывода и M*N операций с памятью.

...