Преобразование дублированного кода JS в функцию более высокого порядка

Aug 20 2020

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

Мой код принимает массив элементов, разбивает их на пакеты по 500 и затем выполняет над ними какую-то работу.

В первой функции он удаляет элементы из базы данных:

Функция удаления:

const deleteDocuments = async (documentReferences) => {
   const batchesOf500 = Math.ceil(documentReferences.length / 500);

   for(let batchNumber = 0; batchNumber < batchesOf500; batchNumber += 1) {
      const batch = getBatchWriter();
      const startingIndex = batchNumber * 500;
      const maxIndex = startingIndex + 500;

      for(let index = startingIndex; index < maxIndex; index += 1) {
         if(index < documentReferences.length) {
            const documentPath = documentReferences[index];
            batch.delete(documentPath);
         }
      }

      await batch.commit();
   }
}

Вторая функция почти идентична, но вместо удаления из базы данных она записывает в базу данных:

Добавить функцию:

const writeToCollection = async (dataArray, collectionRef) => {
   const batchesOf500 = Math.ceil(dataArray.length / 500);

   for(let batchNumber = 0; batchNumber < batchesOf500; batchNumber += 1) {
      const batch = getBatchWriter();
      const startingIndex = batchNumber * 500;
      const maxIndex = startingIndex + 500;

      for(let index = startingIndex; index < maxIndex; index += 1) {
            if(index < dataArray.length) {
               const [key, value] = dataArray[index];
               const doc = getDocFromPath(key);
               batch.set(doc, value);
            }
         }
      }

      await batch.commit();
   }
}

Эти функции почти идентичны, поэтому я написал функцию более высокого порядка, которая выполняет большую часть работы.

Функция высшего порядка:

const runFunctionInBatchesOf500 = (func, dataArray) => {
   const batchesOf500 = Math.ceil(dataArray.length / 500);

   for(let batchNumber = 0; batchNumber < batchesOf500; batchNumber += 1) {
      const batch = this.firestore.batch();
      const startingIndex = batchNumber * 500;
      const maxIndex = startingIndex + 500;

      for(let index = startingIndex; index < maxIndex; index += 1) {
         const document = dataArray[index];
         func(document, batch);
      }
   }

   await batch.commit();
}

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

const write = (document, batch) => {
   const doc = getDocFromPath(key);
   batch.set(doc, value);
};

await runFunctionInBatchesOf500(write, dataArray);

Все это работает, но я думаю, что мне что-то не хватает. Эффективно ли это использование функций высшего порядка? Что могло бы быть более элегантным решением в стиле FP?

Ответы

6 konijn Aug 20 2020 at 14:59

Из небольшого обзора;

  • Зачем жестко кодировать длину пакета до 500?
  • Почему бы не сделать длину пакета постоянной величиной?
  • Вы даже жестко указали длину в имени функции, что действительно прискорбно.
  • batchNumber++ более каноничен, чем batchNumber += 1
  • Я пошел бы за , maxIndex = Math.min(startingIndex + 500, dataArray.length);потому что теперь у вас есть много звонков , чтобы funcс undefinedкак documentзначение
  • awaitтребует runFunctionInBatchesOf500быть async(сейчас отсутствует)
  • Я бы использовал Array.prototype.slice()для создания пакетов в виде массива, а затем использовал бы forEachдля каждого фрагмента / пакета
  • const doc = getDocFromPath(key);<- откуда keyвзялся злой глобал?

Я лично поступил бы мягко, если бы настроил прототип Array так, чтобы я мог продолжать связывание в стиле FP;

Array.prototype.mapSlice = function arrrayMapSlice(n){
  //Just return `this` if we get a non-sensical parameter
  if(isNaN(n) || n <= 0){
    return this;
  }
  let start = 0, out = [];
  while(start < this.length){
    out.push(this.slice(start, start+=n));
  }
  return out;
} 

async function runBatches(list, f, batchSize){
   batchSize = batchSize || 500;
   list.mapSlice(batchSize).forEach(batch => {
     const firestoreBatch = this.firestore.batch();
     batch.forEach(document => f(document, firestoreBatch ));
   });
   await batch.commit();
}