🍪

reduceの使い方がわからなくなっちゃった

2024/01/30に公開

Array.prototype.reduce

配列から一つの要素を作り出すメソッド。


https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

MDNより引用

const array1 = [1, 2, 3, 4];

// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  initialValue
);

console.log(sumWithInitial);
// 10

reduceの第一引数に関数、第二引数に初期値を与える。

array1.reduce(
  <span style="color:#d70910;">    (accumulator, currentValue) => accumulator + currentValue</span>,
     <span style="color:#1453c6;">    initialValue</span>
)

第一引数の関数は配列で繰り返し処理される内容。
accumulatorはこれまでの結果の積み重ねを持ち、currentValueに処理中の配列の値が入る。

今回の使用

配列の中に配列があり、配列の中の配列の中に配列があり.....という複雑な構造のデータ。

今回取得したいのは

  • 配列の中の配列の長さの合計
  • 配列の中の配列の中の長さの合計
  • 配列の中の配列の中のあるプロパティ(has_text)がfalseのデータの数
   const dataArray = [{
      id: '1',
      sections: [
        {
          id: '1',
          boxes: [
            {
              id: '1',
              has_text: false,
            },
            {
              id: '2',
              has_text: true,
            },
          ],
      }...
  ]

⭐️ foreachを使ったコード

export const calculateData = (
  dataArray: []
) => {
  let total1 = 0;
  let total2 = 0;
  let total3 = 0;
  dataArray.forEach((data) => {
    total1 = total1 + data.sections.length;
    data.sections.forEach((section) => {
      total2 = total2 + section.boxes.length;
      section.boxes.forEach((box) => {
        if (!box.has_text) {
          total3 = total3 + 1;
        }
      });
    });
  });
  return {
    total1,
    total2,
    total3,
  };
};

⭐️ reduce使ったコード

export const calculateData = (
  dataArray: []
) => {
  const allData = dataArray.reduce(
    (acc, data) => {
      acc.total1 =
        acc.total1 + data.sections.length;
      data.sections.reduce((_, section) => {
        acc.total2 = acc.total2 + section.boxes.length;
        section.boxes.reduce((_, box) => {
          acc.total3 = box.has_text
            ? acc.total3
            : acc.total3 + 1;
          return 0;
        }, {});
        return 0;
      }, {});
      return acc;
    },
    {
      total1: 0,  
      total2: 0,
      total3: 0,
    }
  );

  return allData;
};

詰まったポイント

・reduceは最終的に何かを返さなければならない。(たとえその値を使わないとしても)

・初期値に数字しか入れられないと勘違いしていた。(オブジェクトでも配列でもなんでもオッケー)

・返す値も数字のみだと勘違いしていた。(オブジェクトでも配列でもなんでもオッケー)

・reduceの引数と、reduceの第一引数の関数で渡す引数がごちゃ混ぜになっていた。

ついでにforEachとmapも復習

  • forEach → 配列のひとつひとつに対して処理をする。何も返さない。
  • map   →配列の一つ一つに対して何らかの処理をして新しく作成した配列を返す。

Discussion