JavaScriptのreduceについて
mapに引き続きreduceについてです。
mapはある配列から新たな配列を作成するための関数でした。
reduceは、ある配列から結果を集めるために使用します。
例えば、以下のような商品データがあるとします。
const products = [
{code: '001', price: 100},
{code: '003', price: 200},
{code: '002', price: 300},
{code: '001', price: 400},
];
これをcodeごとにいくつあるかを集計したいとします。つまり以下のような結果を得たいとします。
{
001: 2,
002: 1,
003: 1
}
reduceは、配列の各要素に対して関数を適用し、その結果を積算器に加算して計算します。
言葉だと難しいですが、図で示すと以下のような感じです。
配列の始めの要素を関数fで処理します。
その結果が黒丸です。この黒丸を次の配列の要素の処理の時に渡し、二つ目の処理の結果を黒丸に加えます。
これを最後の配列の要素まで繰り返します。
先ほどの例の場合は、以下のようなコードになります。
const products = [
{code: '001', price: 100},
{code: '003', price: 200},
{code: '002', price: 300},
{code: '001', price: 400},
];
const result = products.reduce((summary, product) => {
const code = product['code'];
summary = summary === undefined ? 1 : summary + 1;
return summary;
}, {});
console.log(result);
reduceの第一引数に渡している関数が、各配列の要素に対して実行する関数です。
第二引数が積算器の初期値です。上の例ですと{}(空のオブジェクト)です。
はじめの配列の要素{code: '001’, price: 100}に対して関数を適用した結果、積算器(summaryの値)は{001:1}となります。
summaryの推移は以下です。難しく見えますが配列の要素に関数を適用した結果を、summaryに加えているだけです。
//初期値
{}
//一つ目の要素の処理後
{
001: 1
}
//二つ目の要素の処理後
{
001: 1,
003: 1
}
//三つ目の要素の処理後
{
001: 1,
002: 1,
003: 1
}
//最後の要素の処理後
{
001: 2,
002: 1,
003: 1
}
補足
今回の例をmapを使ってさらに柔軟性を持たせてみます。
const getCode = product => product.code
const summarizeCode = (summary, code) => {
summary = summary === undefined ? 1 : summary + 1;
return summary;
}
const products = [
{code: '001', price: 100},
{code: '003', price: 200},
{code: '002', price: 300},
{code: '001', price: 400},
];
// 今回の処理はこれ
const result = products.map(getCode).reduce(summarizeCode, {})
console.log(result);
productからcodeを取得する処理としてgetCodeを、codeを集計する処理をsummaryCodeという関数に切り出しました。
あとは、mapとreduceの引数にこれらの関数を渡すだけです。
このようにすることで、products.map(getCode).reduce(summarizeCode, {}) の部分は、各関数の実装をみなくても、これだけみてなんとなく何をしているか分かると思います。
このように可読性が上がります。
さらに、getCodeやsummarizeCodeという関数は、純粋な関数(※1)なため再利用も簡単です。
※1 同じ入力に対して常に同じ結果を返して、関数の外には影響は与えない
関数の引数に関数を渡せるということが、可読性や再利用性の向上の助けになることがなんとなくイメージできれば思います。
最近のコメント