意外と知られていないJavaScriptのreduceの使用方法

reduceとは

配列に対してcallback関数を次の4つの引数を渡しながら一度だけ実行するメソッド。

  • accumulator
  • currentValue
  • currentIndex
  • array

これだけだとわかりづらいので実際のコードを見たほうが理解が早い。

実際にはcurrentIndexとarrayは使用されず、accumulatorとcurrentValueだけ使用されることがほとんどだ。

var r = [0, 1, 2, 3].reduce((accumulator, currentValue, currentIndex, array) => {
  console.log(accumulator, currentValue, currentIndex, array)
  // 0 1 1 [0, 1, 2, 3]
  // 1 2 2 [0, 1, 2, 3]
  // 3 3 3 [0, 1, 2, 3]
  return accumulator + currentValue
})
console.log(r)
// => 6

reduceにはこのように初期値も設定できる。

var r = [0, 1, 2, 3].reduce((a, c) => {
  return a + c
}, 10)
console.log(r)
// => 16

accumulatorの初期値は設定されていないと配列の最初の値になる。

勘違いされることが多いが省略すれば初期値が0になるわけではない。

reduceは合計の算出以外にも使える

reduceのサンプルは配列内の合計を算出するものが多いので、それ以外の用途の認知度が低い。

実際はほかにも色々と用途がある。

配列内の文字列の最大文字数を取得

reduceを使用して配列内の文字列の最大文字数を取得できる。

var words = ['apple', 'banana', 'car']
var checkLen = (len, word) => {
  return len < word.length ? word.length : len
};
var result = words.reduce(checkLen, 0)
console.log(result)
// => 6

ただし、スプレッド構文が使用可能であれば下記のコードのほうがより短く書ける。

var arr = ['apple', 'banana', 'car']
var maxLen = Math.max(...(arr.map(v => v.length)))
console.log(maxLen)
// => 6

二次元配列を一次元配列に変換

reduceを使用すれば二次元配列を一次元配列に変換できる。

var arr = [[1, 2], [3, 4]]
var result = arr.reduce((a, b) => a.concat(b))
console.log(result)
// => [1, 2, 3, 4]

配列オブジェクトの数値の合計値を取得

reduceを使用すれば配列オブジェクトの数値の合計値を簡単に取得できる。

var data = [
  {
    name: 'foo',
    money: 1000
  },
  {
    name: 'bar',
    money: 2000
  },
  {
    name: 'baz',
    money: 3000
  }
]
var sum = data.reduce((a, c) => {
  return a + c.money;
}, 0)
console.log(sum)
// => 6000

配列オブジェクトの条件別の合計値を取得

前述のdataに性別のkeyとvalueを追加して男女別のmoneyの合計値を計算したい場合、mapなどを使用するとコードが長くなりがちだが、reduceを使用すると短くわかりやすく書ける。

var data = [
  {
    name: 'foo',
    money: 1000,
    sex: 'female'
  },
  {
    name: 'bar',
    money: 2000,
    sex: 'male'
  },
  {
    name: 'baz',
    money: 3000,
    sex: 'female'
  },
  {
    name: 'hoge',
    money: 4000,
    sex: 'male'
  }
]
var result = data.reduce((a, c) => {
  c.sex === 'female' ?
    a['female'] += c.money :
    a['male'] += c.money
  return a
}, {female: 0, male: 0})
console.log(result)
// => {female: 4000, male: 6000}

オブジェクトの数値の合計値を取得

reduceは配列に使用するものなのでオブジェクトの合計値の計算はできないと思われがちだが、実際はObject.keysを使用すればオブジェクトの合計値を計算できる。

var data = {
  foo: {
    money: 1000
  },
  bar: {
    money: 2000
  },
  baz: {
    money: 3000
  },
}
var result = Object.keys(data).reduce((a, c) => {
  return a + data[c].money;
}, 0);
console.log(result)
// => 6000

初期値を空配列にしてpushで入れる

reduceの初期値を空配列[]にしてpushで配列オブジェクトの値を入れて配列化するという使い方もある。

var data = [
  {
    name: 'foo',
    money: 1000
  },
  {
    name: 'bar',
    money: 2000
  },
  {
    name: 'baz',
    money: 3000
  }
]
var result = data.reduce((a, c) => {
  a.push(c.name)
  return a
}, [])
console.log(result)
// => ["foo", "bar", "baz"]

以下のようにifで条件分岐すれば特定の条件に合致したものだけを配列に入れられる。

var data = [
  {
    name: 'foo',
    money: 1000
  },
  {
    name: 'bar',
    money: 2000
  },
  {
    name: 'baz',
    money: 3000
  }
]
var result = data.reduce((a, c) => {
  if (c.money >= 2000) {
    a.push(c.name)
  }
  return a
}, [])
console.log(result)
// => ["bar", "baz"]