Array.prototype.reduce()メソッドの使い方

Array.prototype.reduce()メソッドの使い方

おはようございます!

今回は JavaScript の reduce メソッドについて調べる機会があったのでまとめてみました。

reduce メソッドとは?

reduce() メソッドは、配列の各要素に対して (引数で与えられた) reducer 関数を実行して、単一の出力値を生成します。

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

ざっくり言うと、reduce メソッドは配列の各要素をまとめて一つの値を返すメソッドです。

どのような動きをするのか簡単な例を上げて見ていきます。

reduce メソッドの構文

arr.reduce( callback( accumulator, currentValue[, index[, array]] ){
  // 実行した結果を返す
}[, initialValue]);

配列の各要素に対して実行されるコールバック関数は、以下 4 つの引数を持ちます。

  1. accumulator:コールバック関数の戻り値が格納される引数
  2. currentValue:現在処理されている要素の値
  3. [, index]:現在処理されている要素のインデックス。[, initialValue] が指定された場合は 0 から始まり、指定されなかった場合は 1 から始まる(省略可)
  4. [, array]:対象となっている配列(省略可)

次に、reduce メソッドに渡す第二引数です。

  1. [, initialValue]:コールバック関数が実行されるときに最初に呼び出される引数(省略可)

値を合計して返す

まずは配列の要素を合計する例から見ていきます。

const array = [1, 2, 3, 4, 5];
const result = array.reduce((accu, curr, index) => {
  return accu + curr;
});
console.log(result); // 15

引数の中身を出力して詳しく見ていきましょう。

reduce メソッドの第二引数に値が無い場合

const array = [1, 2, 3, 4, 5];
const result = array.reduce((accu, curr, index, array) => {

console.log("accu: " + accu + " curr: " + curr + " index: " + index + " array: " + array);
// 'accu: 1 curr: 2 index: 1 array: 1,2,3,4,5'
// 'accu: 3 curr: 3 index: 2 array: 1,2,3,4,5'
// 'accu: 6 curr: 4 index: 3 array: 1,2,3,4,5'
// 'accu: 10 curr: 5 index: 4 array: 1,2,3,4,5'

return accu + curr;
});
console.log(result); // 15

reduce メソッドの第二引数に値を指定した場合

reduce 関数の第二引数を指定した場合と比べてみます。(※引数 array は省略)

const array = [1, 2, 3, 4, 5];
const result = array.reduce((accu, curr, index) => {
  console.log("accu: " + accu + " curr: " + curr + " index: " + index);

  // 'accu: 0 curr: 1 index: 0' accuに 0 + 1 の結果が入っている
  // 'accu: 1 curr: 2 index: 1' accuに 1 + 2 の結果が入っている
  // 'accu: 3 curr: 3 index: 2' accuに 3 + 3 の結果が入っている
  // 'accu: 6 curr: 4 index: 3' accuに 6 + 4 結果が入っている
  // 'accu: 10 curr: 5 index: 4' accuに 10 + 5 結果が入っている

  return accu + curr;
}, 0); // <-- 引数を指定
console.log(result); // 15

引数を指定した場合は、accumulator の初期値に引数の値が入っています。そして、currentValue には配列の 1 つ目の要素が代入されています。index の値も配列の先頭から始まっていることが分かります。

引数を指定しなかった場合は、accumulator の配列の一番最初の要素が入り、currentValue には配列の二番目の要素が入っていることが分かります。

最終的に返す値に変化ありませんが、reduce 関数の第二引数を指定するかしないかで動きが変わってくるので注意が必要です。

配列の中で最大値、最小値を求める

配列に格納されている値の最大値と最小値を取得したいケースです。

const array = [1, 2, 8, 4, 3, 10];
const result = array.reduce((accu, curr, index, array) => {
  console.log('accu: ' + accu + ' curr: ' + curr + ' index: ' + index);

  // 'accu: 0 curr: 1 index: 0'
  // 'accu: 1 curr: 2 index: 1'
  // 'accu: 2 curr: 8 index: 2'
  // 'accu: 8 curr: 4 index: 3'
  // 'accu: 8 curr: 3 index: 4'
  // 'accu: 8 curr: 10 index: 5'

  if (accu > curr) {
    return accu;
  } else {
    return curr;
  }
}, 0);

console.log(result); // 10

accumulator と currentValue を比較して、値が大きかったほうを返しています。条件を逆にすれば最小値を求めることができますが、ここでは [, initialValue] の初期値に 0 が指定されているため、最小値は 0 になることに注意が必要です。

二次元配列を一次元配列にする(フラット化する)

以下のように使用すると、二次元配列を一次元配列にすることが出来ます。

const array = [
  ['バナナ', 'みかん'],
  ['いちご', 'ぶどう', 'メロン'],
];
const result = array.reduce((accu, curr) => {
  console.log('accu: ' + accu + ' curr: ' + curr); // 'accu: バナナ,みかん curr: いちご,ぶどう,メロン'
  return accu.concat(curr);
});

console.log(result); // [ 'バナナ', 'みかん', 'いちご', 'ぶどう', 'メロン' ]

値を右から左に処理する reduceRight メソッド

reduce メソッドが配列の要素に対して左から右に処理を行うのに対して、reduceRight メソッドは右から左に処理を行います。

先程の二次元配列を一次元配列にする処理で動作を確認します。

const array = [
  ['バナナ', 'みかん'],
  ['いちご', 'ぶどう', 'メロン'],
  ['りんご', 'はっさく'],
];
const result = array.reduceRight((accu, curr) => {
  console.log('accu: ' + accu + ' curr: ' + curr); // 'accu: りんご,はっさく curr: いちご,ぶどう,メロン' // 'accu: りんご,はっさく,いちご,ぶどう,メロン curr: バナナ,みかん'
  return accu.concat(curr);
});

console.log(result); // [ 'りんご', 'はっさく', 'いちご', 'ぶどう', 'メロン', 'バナナ', 'みかん' ]

先程とは違って accumulator に配列の末尾の要素が入り、末尾から 2 番めの要素が currentValue に入っています。

その後は右から順番に最後の要素まで処理を行っています。返却される配列の順番も変わっていることがわかります。

おわりに

列に対して処理を行うメソッドでは、map() や filter() などがありますが、同じことが reduce() でも出来ます。reduce() の方が応用が効く処理が実装できますが、簡単な処理であればその他のメソッドを使うほうが良いかもしれません。