関数の魅力を引き出すmap, filter, reduce
JavaScriptの関数は第一級オブジェクトである。
変数に関数を代入できるし、関数の引数に関数を与えられるし、関数の返り値として関数を返せる。
ES2015で追加されたmap
, filter
, reduce
を使うと今まで何だか綺麗に書けなかったものがスッキリいい感じに書けるようになる。
よくあるJSON配列をごにょごにょと変換する例を挙げていこう。
対象のJSON配列:
const list = [ { id : "1", name : "hoge", status : "active", num : 13, children : ["a", "c", "e"] }, { id : "2", name : "fuga", status : "stop", num : 22, children : ["a", "g"] }, { id : "3", name : "piyo", status : "active", num : 57, children : ["e", "h"] } ];
map
map
を使うと、ある配列を新しい配列に変換できる。
要素数を変えずに他の配列書き換えたい場合に便利。
idだけを抜き出した配列を作りたい
従来の書き方
var ids = []; for (var i = 0; i < list.length; i++) { ids.push(list[i].id); } console.log(ids); // [ '1', '2', '3' ]
map
を使った書き方
const ids = list.map((item) => item.id); console.log(ids); // [ '1', '2', '3' ]
idとnameだけを抜き出した配列を作りたい
従来の書き方
var ids = []; for (var i = 0; i < list.length; i++) { ids.push({ id : list[i].id, name : list[i].name }); } console.log(ids); // [ { id: '1', name: 'hoge' }, // { id: '2', name: 'fuga' }, // { id: '3', name: 'piyo' } ]
map
を使った書き方
const ids = list.map(({id, name}) => ({id, name})); console.log(ids); // [ { id: '1', name: 'hoge' }, // { id: '2', name: 'fuga' }, // { id: '3', name: 'piyo' } ]
filter
filter
を使うと、ある配列から必要なものだけを選んだ新しい配列を作ることができる。
statusがactiveのものに絞り込んでidを取得したい
従来の書き方
var activeList = []; for (var i = 0; i < list.length; i++) { if (list[i].status === 'active') { activeList.push(list[i].id); } } console.log(activeList); // [ '1', '3' ]
filter
を使った書き方(map
も用いる)
const activeList = list.filter((item) => item.status === 'active') .map((item) => item.id); console.log(activeList); // [ '1', '3' ]
statusがactiveのものがあるかどうかをBool値で取得したい
従来の書き方
var flag = false; for (var i = 0; i < list.length; i++) { if (list[i].status === 'active') { flag = true; } } console.log(flag); // true
filter
を使った書き方(map
も用いる)
const flag = list.filter((item) => item.status === 'active') .length > 0; console.log(flag); // true
reduce
reduce
は配列内の全要素を用いて値やオブジェクトを生成する。
使い方が難しいが、使いこなせると強い。
各要素が保持しているnumの合計値を取得したい
従来の書き方
var sum = 0; for (var i = 0; i < list.length; i++) { sum += list[i].num; } console.log(sum); // 92
reduce
を使った書き方(map
も用いる)
const sum = list.map((item) => item.num) .reduce((x, y) => x + y); console.log(sum); // 92
各要素が保持しているchildren配列を連結したい
従来の書き方
var children = []; for (var i = 0; i < list.length; i++) { children = children.concat(list[i].children); } console.log(children); // [ 'a', 'c', 'e', 'a', 'g', 'e', 'h' ]
reduce
を使った書き方(map
も用いる)
const children = list.map((item) => item.children) .reduce((x, y) => [...x, ...y]); console.log(children); // [ 'a', 'c', 'e', 'a', 'g', 'e', 'h' ]
まとめ
for
文やforEach
文はループ処理であり、returnが存在しないため、ループ処理後は一度式を途切らせなくてはならない。
しかし、map
やfilter
やreduce
を用いると次の処理にそのままつなげることができる。
関数型の入り口として知っておいて損はない。
また、ほとんどの処理を1行で書くことができるため、コードが簡潔で済む。
慣れていないと解読する側は大変だが、map
、filter
、reduce
さえ抑えておけば、後々活きてくるだろう。