🗺️

reduceとMapを組み合わせてコードをすっきりさせる

2023/11/04に公開
2

Mapを作りたい時

テーブルからfecthしたデータをMap化して高速にアクセスしたいケースはよくあると思います。

そのMapを作る時、reduceを使わない場合はこうなるかと。

const users = await fetch("http://example.com/users");
const userMap = new Map<string, UserEntity>();
users.forEach((user) => {
    userMap.set(user.id, user)
})

reduceを使うとちょっとすっきりします

const users = await fetch("http://example.com/users");
const userMap = users.reduce((accum, current) => {
    accum.set(current.id, current)
}, new Map<string, UserEntity>())

単純な数の集計をしたい

reduce,Mapの組み合わせで集計もすっきり記述できるかと思います。

const result = [
  { category: "x", count: 10 },
  { category: "x", count: 10 },
  { category: "y", count: 10 },
  { category: "x", count: 10 },
  { category: "z", count: 10 },
  { category: "y", count: 10 }
];

const sum = result.reduce((accum, current) => {
  accum.set(current.category, (accum.get(current.category) ?? 0) + current.count);
  return accum;
}, new Map());

console.log(sum);

/* 結果は以下の通り
 Map {}
 x: 30
 y: 20
 z: 10
*/


<追記:2023/12/03>
集計をする場合はgroupByを使うと良いケースも
https://qiita.com/silane1001/items/5f2a4f06e964e7443433

Discussion

standard softwarestandard software

categoryにx,y,z以外がきたときに、処理を途中で止めてください。
という仕様がきたときに100万項目とかある場合に、reduceは対応しきれなくて
仕様変更に弱いため、下記のごくごく自然なループコードの方がよいと思います。

const result = [
  { category: "x", count: 10 },
  { category: "x", count: 10 },
  { category: "y", count: 10 },
  { category: "x", count: 10 },
  { category: "z", count: 10 },
  { category: "y", count: 10 }
];

const map = new Map();
for (const item of result) {
  map.set(
    item.category,
    (map.get(item.category) ?? 0) + item.count
  );
}

console.log(...map.entries())
// ["x", 30] ["y", 20] ["z", 10]
jlmn1026jlmn1026

100万項目とかある場合

全量が見切れているケースでしか使うべきではないでしょうね。