重複する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();
   }
}

2番目の関数はほとんど同じですが、データベースから削除する代わりに、データベースに書き込みます。

関数の追加:

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持つundefineddocument
  • awaitする必要runFunctionInBatchesOf500がありますasync(今はありません)
  • 私が使用するArray.prototype.slice()配列としてバッチを作成し、その後、使用しforEach、各スライス/バッチに
  • const doc = getDocFromPath(key);<-key邪悪な世界はどこから来たのですか?

個人的には、アレイプロトタイプを調整して、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();
}