【JavaScript】オブジェクトをmap()とかするにはRamdaが便利

lodash みたいな便利関数のライブラリ Ramda はちょっと変わり種だけどけっこう便利。

特徴としては、

  • 関数型の思想に基づいている
  • なので関数に副作用がない
  • ほとんどの関数が配列とオブジェクトの両方に適用できる

というのがある。

JavaScript は配列の操作は最近わりとやりやすくなってきたけど、オブジェクトの操作は正直まだやりにくい。オブジェクトを操作する機会なんて日常茶飯事なのに、オブジェクトの中身をごにょごにょするためにいったん配列にしてから操作してオブジェクトに戻す、みたいなことをしなくちゃいけなかったりする。Ramda を使えばその辺を簡潔に読みやすく書ける。

ふつうの JavaScript でオブジェクトを map() する

たとえばプレーンな JavaScript の配列には map() 関数があって、

const doubled = [1, 2, 3].map(x => x * 2)
console.log(doubled)
// -> [2, 4, 6]

というふうに使える。

ではオブジェクトに対して同じことをしようとしたらどうなるだろうか。一度配列にしてから操作することになるだろう。次の例ではオブジェクトの中の値をすべて2倍にしている。

const obj = { key1: 1, key2: 2, key3: 3 }
const doubled = Object.entries(obj)
  .map(([key, value]) => [key, value * 2])
  .reduce((obj, [key, value]) => ({...obj, [key]: value}), {})
console.log(doubled)
// { key1: 2, key2: 4, key3: 6 }

頑張って読めばわかる。でも読みにくい。

Ramda でオブジェクトを map() する

同じことを Ramda でやってみる。まずは配列から。特に変わったところはない。

const {map} = require('ramda')
const doubled = map(x => x * 2)([1, 2, 3])
console.log(doubled)

違いといえば、Ramda の map() 関数は関数を作る関数だというところだ。map(x => x * 2) は新たな関数を作っている。配列の中身を2倍にする関数だ。これは次のように分けて書ける。

const {map} = require('ramda')
const double = map(x => x * 2)
const doubled = double([1, 2, 3])
console.log(doubled)

そして、次にオブジェクト。オブジェクトの map() は実は上の double 関数をそのまま使える。

const {map} = require('ramda')
const double = map(x => x * 2)
const doubled = double({key1: 1, key2: 2, key3: 3})
console.log(doubled)

これでいい。簡潔だし、読みやすい。最初の例のように、オブジェクト→配列→オブジェクトという遠回りをする必要がない。

Ramda は独特

Ramda は他にもいろんな関数があるし、カリー化とか関数の合成とかいろんなことができる。どっぷり使おうとすればそれなりに手間をかけて学ばないといけないけど、あんまり過激に関数型チックに使わなくてもこういうライトな使い方もできる。