【JavaScript】Arrayのreduce関数を自由に使いこなす
配列の reduce
関数は、配列から一つの何か(オブジェクト、数値、文字列)を作りたい時に使います。
いつものように具体例から。
let array = [1, 2, 3, 4, 5] let sum = array.reduce(function (sum, num) { return sum + num }, 0) console.log(sum) // 15
配列内のすべての数の和を出しました。 reduce(関数, 初期値)
という形で使います。 reduce
に渡す関数を理解するのは少しやっかいです。
と、その前に同じことをアロー関数で書き直しておきます。
let sum = array.reduce((sum, num) => sum + num, 0)
reduce
に渡した関数 (sum, num) => sum + num
について。 第一引数 sum
は「前までに評価された値」で、第二引数 num
は「次に渡される配列の値」です。わかりにくい。
reduce
関数は、他の map
関数や filter
関数と同じように、配列の要素を先頭から一つずつ取り出して、何らかの処理をします。 reduce
関数がやりたいことは、「配列の要素それぞれに同じ関数を実行して最終的に一つの値に還元する(reduce
する)」ということです。たとえば、配列の要素それぞれに「足す」という関数を実行して総和を求めるといったふうに。
先ほどの sum を for 文で書くと以下のようになります。
let sum = 0 let plus = (sum, num) => sum + num for (let num of array) { sum = plus(sum, num) } console.log(sum) // 15
reduce
で_.keyBy
を自前実装
lodash に入っているいくつかの関数は、 reduce
を使えば自前でパパっと実装できます。
_.keyBy は、オブジェクトの配列から、あるプロパティをキーとしたオブジェクトを作ります。
たとえば、次のようなオブジェクトの配列があったとして、
let books = [ { id: 'aaa', title: 'Book1' }, { id: 'bbb', title: 'Book2' }, { id: 'ccc', title: 'Book3' } ]
これを、 id をキーとしたオブジェクトにしたい。
let bookMap = { aaa: { id: 'aaa', title: 'Book1' }, bbb: { id: 'bbb', title: 'Book2' }, ccc: { id: 'ccc', title: 'Book3' } }
この変換は、 _.keyBy
を使うとこう書けます。
const _ = require('lodash') let bookMap = _.keyBy(books, 'id')
それで、同じことを reduce
を使うとこうなります。
let bookMap = books.reduce( (map, book) => Object.assign(map, { [book.id]: book }), {} )
何をしているかというと、まず初期値に空のオブジェクト {}
をセットする。次に book を一つずつ取り出して、オブジェクトに { [book.id]: book }
というキー・バリューを追加しているわけです。
慣れればあまり考えずに書けるようになります。が、正直、知っていないと可読性が低いというか解読に時間がかかるため、そういう意味では lodash
を使ったほうがすっきりはします。
reduce
で_.groupBy
を自前実装
次は、_.groupBy
を実装してみます。(第二引数に文字列を渡すやつだけ。)
let array = ['one', 'two', 'three'] let group = _.groupBy(array, 'length') console.log(group) // { '3': ['one', 'two'], '5': ['three'] }
配列内の各オブジェクトのプロパティで分類して、プロパティの値をキーとしたオブジェクトにしています。これを reduce
で書くと、次のようになります。
// length で groupBy する let group = array.reduce( (group, str) => Object.assign(group, { [str.length]: (group[str.length] || []).concat(str) }), {} )
groupBy
をそのまま実装したければ、次のような感じでしょうか。
// property は文字列 function groupBy (array, property) { return array.reduce( (group, item) => Object.assign(group, { [item[property]]: (group[item[property]] || []).concat(item) }), {} ) }
reduce
で_.uniq
を自前実装
_.uniq
も実装できます。重複のない配列に変換するメソッドです。
let array = [1, 2, 2, 3, 2, 1] _.uniq(array) // [1, 2, 3]
reduce
を使うと…
function uniq (array) { let uniqSet = array.reduce( (set, item) => set.add(item), new Set() ) return Array.from(uniqSet) }
いったん Set
にして重複をなくし、それから配列に直しています。
こんな感じで、 reduce
を使って lodash
のメソッドを自前実装してみると、 reduce
の使い方が手に馴染んできますね。