ユーニックス総合研究所

  • home
  • archives
  • js-reduce

JavaScriptのreduceの使い方: 配列を走査し値を蓄積する

  • 作成日: 2020-08-15
  • 更新日: 2023-12-28
  • カテゴリ: JavaScript

JavaScriptのreduceの使い方

JavaScriptの配列にはreduceメソッドがあります。
これの使い方を解説します。
reduce()を使うと配列を走査することができます。
またreduce()map()filter()などと比べると少し特殊なメソッドです。

↓はreduceのサンプルコードです。

const arr = [1, 2, 3]  
const result = arr.reduce((acc, cur) => acc + cur)  

console.log(result)  
// 6  

関連記事

reduceの動作

reduceは最初はわかりづらいですが、原理がわかれば簡単です。
reduceは第1引数に関数を渡せます。
この関数の第1引数には「蓄積された値」、第2引数には「現在の要素」が渡されます。
「蓄積された値」は関数の戻り値のことです。

たとえば[1, 2, 3]という配列のreduceを呼び出したとします。
そうするとreduceは配列の先頭の要素から順に辿っていきます。

const arr = [1, 2, 3]  
const result = arr.reduce((acc, cur) => acc + cur)  

console.log(result)  
// 6  

acc(accumulator)には初期値として配列の先頭の要素が渡されます。
そしてcur(current value)にはその次の要素が渡されます。
関数の戻り値は次の走査でaccになります。

↓の動作を見てみてください。

const arr = [1, 2, 3, 4]  
const result = arr.reduce((acc, cur) => {  
    console.log(`acc: ${acc}, cur: ${cur}`)  
    return acc + cur  
})  

console.log(result)  

出力結果↓。

acc: 1, cur: 2  
acc: 3, cur: 3  
acc: 6, cur: 4  
10  

[1, 2, 3, 4]という配列がreduce()で走査されます。
accには蓄積されていく値が渡され、curには現在の配列の要素が渡されます。
accは配列の1番目の要素の値から始まり、curは配列の2番目の要素から渡されます。
このようにreduce()はちょっと使い方が独特です。

🦝 < 面食らうよね

🐭 < 変わったメソッドだぁ

Accumulatorって?

accは何なのかというとAccumulator(アキュムレーター)の略です。
Accumulatorは和訳すると

蓄積者、蓄財家、蓄電池、累算器

のような意味を持ちます。
つまり計算の結果を溜めていく蓄財家ということですね。

reduceでは関数のaccという引数に走査した結果の値が蓄積されていきます。
このaccに加算される値は関数の返り値になります。

reduceのコールバック関数

reduceの第1引数に渡せるのはアロー関数普通の関数です。

const arr = [1, 2, 3]  
const result = arr.reduce((acc, cur) => {  
    return acc + cur  
})  

↑の場合はアロー関数になります。

const arr = [1, 2, 3]  
const result = arr.reduce(function (acc, cur) {  
    return acc + cur  
})  

↑の場合は普通の関数です。
アロー関数と普通の関数を使った場合の違いはthisの参照先が異なる点です。
アロー関数はthisは関数の外になりますが、普通の関数はthisは関数の中になります。

reduceのコールバック関数の引数

reduceに渡す関数の引数は↓のようになっています。

  • 第1引数: 蓄積された値
  • 第2引数: 現在の要素
  • 第3引数: 現在の添え字
  • 第4引数: 走査中の配列

これらは簡単なコードで確認することが出来ます。

const arr = [1, 2, 3, 4]  
const result = arr.reduce((acc, cur, index, me) => {  
    console.log(`acc: ${acc}, cur: ${cur}, index: ${index}, me: ${me}`)  
    return acc + cur  
})  

↑のコードの結果↓。

acc: 1, cur: 2, index: 1, me: 1,2,3,4  
acc: 3, cur: 3, index: 2, me: 1,2,3,4  
acc: 6, cur: 4, index: 3, me: 1,2,3,4  

添え字が1から始まってる点に注意してください。
つまり配列の要素(curの値)は添え字1から(2番目から)始まります。
添え字0の要素はreduceの初期値(accの値)として使われています。

meには走査中の配列が渡されます。
つまり↑の場合はarrmeは同じものになります。

reduceの初期値

reduceには初期値を明示的に渡せます。
reduceの第2引数に初期値を渡すと、reduceはその初期値から走査を開始します。

const arr = [1, 2, 3, 4]  
const result = arr.reduce((acc, cur) => {  
    console.log(`acc: ${acc}, cur: ${cur}`)  
    return acc + cur  
}, 10)  

console.log(result)  
// 20  
acc: 10, cur: 1  
acc: 11, cur: 2  
acc: 13, cur: 3  
acc: 16, cur: 4  
20  

↑の場合、走査する配列は[1, 2, 3, 4]で合計は10ですが、初期値が10になっているので計算の結果は20になります。
1010 + 111に、そして1111 + 213に、という感じでaccが更新されていきます。

reduceの使用例

reduceの使用例を紹介します。

配列の合計値を得る

すでに何度も紹介してますが、配列の合計値を得るには↓のようにします。

const arr = [1, 2, 3, 4]  
const result = arr.reduce((acc, cur) => acc + cur)  

console.log(result)  
// 10  

reduceを使わずに普通のfor文を使った場合は↓のようなコードになります。

const arr = [1, 2, 3, 4]  
let result = 0  

for (const el of arr) {  
    result += el  
}  

console.log(result)  
// 10  

またmap()を使う方法もあります。
map()を使った場合は↓のようなコードになります。

const arr = [1, 2, 3, 4]  
let result = 0  

arr.map(el => {  
    result += el  
})  

console.log(result)  
// 10  

関連記事
JavaScriptのmapの使い方: 配列に一括処理をして新しい配列を作成する

map()は本来は配列から別の配列を生成するメソッドです。
しかし↑のように配列を走査したいような処理にも使うことができます。

効率的には普通のfor文を使った方がいいかもしれません。
理由はmap()は新しい配列を生成するからです。
しかし計測はしてないので実際はどうかはわかりません。

要素の出現回数を数える

配列の要素の出現数を数えて、それらを保存したオブジェクトを返すサンプルです。
統計に使えそうですね。

const arr = ['Cat', 'Dog', 'Bird', 'Cat']  
const result = arr.reduce((acc, key) => {  
    if (key in acc) {  
        acc[key]++  
    } else {  
        acc[key] = 1  
    }  
    return acc  
}, {})  

console.log(result)  
// { Cat: 2, Dog: 1, Bird: 1 }  

参考: Array.prototype.reduce() - JavaScript | MDN

reduceを使わない場合は↓のようなコードになります。

const arr = ['Cat', 'Dog', 'Bird', 'Cat']  
let result = {}  

for (const key of arr) {  
    if (key in result) {  
        result[key]++  
    } else {  
        result[key] = 1  
    }  
}  

console.log(result)  
// { Cat: 2, Dog: 1, Bird: 1 }  

心なしかreduce()を使った方が処理が複雑に見えますね。
このケースでは普通のfor文を使った方が良いかもしれません。

2次元配列を1次元配列にする

2次元配列を1次元にして平らにするサンプルです。

const arr = [[1, 2], [3, 4]]  
const result = arr.reduce((acc, el) => {  
    return acc.concat(el)  
}, [])  

console.log(result)  
// [ 1, 2, 3, 4 ]  

参考: Array.prototype.reduce() - JavaScript | MDN

reduceを使わない場合は↓のようなコードになります。

const arr = [[1, 2], [3, 4]]  
let result = []  

for (const el of arr) {  
    result = result.concat(el)  
}  

console.log(result)  
// [ 1, 2, 3, 4 ]  

このケースではreduce()が効率的に使われてるように見えます。
acc.concat(el)の返り値を有効利用しているのでおもしろいですよね。

問題

Q1: reduceの走査で、最初に関数が呼ばれる時のaccの値として適当なものを答えよ

  1. 配列の1番目(0オリジン)の要素
  2. 配列の0番目(0オリジン)の要素
  3. reduceの第2引数

Q2: reduceに渡すコールバック関数の第4引数として適当なものを答えよ

  1. 蓄積された値
  2. 添え字
  3. 走査中の配列

答え
Q1: 2, 3
Q2: 3