Ramdaは関数型プログラミングを補助するJavaScriptライブラリだけど、それとは別に便利な関数もたくさん入っている。便利なものをカウントダウン形式に10個紹介するよ。気になる関数があればAPIドキュメント: http://ramdajs.com/docs/ を見てみよう。
#01. path: 安全なプロパティアクセス
Objectを連想記憶/辞書型/ハッシュテーブルとして使う際に、深いところにあるデータを抜き取るのがめんどくさい。
const obj = {
cache: {
user: {
id: 123,
name: 'alice',
},
contents: {
...
},
}
例えば上記の例だと、名前をとってくるのには name = obj.cache.user.name
とやればいいんだけど、例えば obj.cache.user
というキーが存在しない場合は、例外がスローされちゃう。めんどい。そんな時は R.path
の出番。
R.path(['cache','user','name'], obj) // -> 'alice'
R.path(['cache', 'user', 'name'], { cache: {} }) // -> undefined
R.path(['cache', 'user', 'name'], {}) // -> undefined
#02. range: 連続する数値の生成
[1,2,3,4,5]
などのように連続するN個の整数からなる配列を作るのが有効な場面がある。range
を使うとすぐ作れる。N回なんかの処理をしたい!とか、オフセットを付けたいとか。
// 1+2+3+....10
R.range(1, 11).reduce((a, x) => a + x, 0); // -> 55
// generate url
R.range(1,5).map(i => `/index?page=${i}`) // -> ['/index?page=1','/index?page=2'... ]
関連: 同じものを何個も作りたい、ってときは repeat もあるけど、癖があるので注意。
#03. scan: reduceっぽい何か
reduce
は、配列の値を1個ずつ折りたたんで最終的に1つの値にするけど、scan
は同じように折りたたむんだけど、途中経過を配列に保持してくれる。reduceのデバッグに使うのもよし、以下の例のように、いい使い道もあるかも。
// calc factorial
R.scan((a, x) => a * x, 1, R.range(1, 6)) // [ 1, 1, 2, 6, 24, 120 ]
#04. clone: ディープコピー
Ramda もディープコピー機能を提供している。普通はLodashとか使うのかもしれないけど、せっかくRamdaをインポートしてるなら使うのもいいかもね。
const obj1 = { a: [1, 2] };
const obj2 = R.clone(obj1)
obj2.a.pop();
obj1.a.length === obj2.a.length // -> false
#05. pipe: 関数をつなげてつなげて
左から右にどんどん関数を適用していくパターン。最初の関数以外は、引数を1つしかとってはいけない。
const getName = obj => obj.name;
const getMsg = name => `hello ${name}`;
const toUpper = str => str.toUpperCase();
const toLoud = str => str.concat('!');
const f = R.pipe(getName, getMsg, toUpper, toLoud);
f({ name: 'alice' });// -> HELLO ALICE!
右から左にどんどん関数を適用するのは compose
だけどこっちのほうが直観的に使える。
#06. once: 絶対に2回以上呼ばせない
何回呼んでも、実際には 2回目以降は呼ばないというスゴ技。返り値は1回目のものが使いまわされる。
const greetOnce = R.once(() => { console.log('hello'); });
greetOnce()
greetOnce()
greetOnce()
greetOnce()
// hello is printed only once.
#07. memoizeWith: 関数呼び出し結果の保存
もし作った関数がpureであれば、同じ引数で何回も関数呼び出しをするのは計算リソースの無駄づかい。1回関数呼び出しをしたら返り値を覚えておいて、2回目以降は覚えておいた返り値を渡すようにする。
const echo = (val) => { console.log(`calc ${val}`); return val;}
const ecoEcho = R.memoizeWith(R.identity, echo);
ecoEcho(1);
ecoEcho(1);
ecoEcho(2);
// print two lines only.
第一引数は、キー作成ロジックであり、よくわからなければR.identity
(引数をそのままキーにする)にしておけばよい。
#08. allPass: N回filterする
isXXX()
というようなチェックを何回もするような時、例えば、配列を何回もfilterするようなときは、allPassを使うとすっきりした見た目になる。すっきりした見た目はエラーがすくなく、コメントの必要すらなくなる
const isNegative = d => d < 0;
const isEven = d => d % 2 === 0;
const isAllIWant = R.allPass([isNegative, isEven]);
R.range(-4, 10).filter(isAllIWant); // -> [ -4, -2 ]
#09. groupBy: 配列要素をグループ化
配列の要素をいくつかのグループに分けたいことがある。素数とそうでない数だとか、スコアによってAクラス〜Dクラスに分けるとか。groupBy
はそのための自然な手段を提供してくれる
const byTruth = R.groupBy(o => (o ? 'T' : 'F'));
const arr = [1, 0, -1, '', undefined, null, {}, [], 'hello'];
byTruth(arr);
// -> { T: [ 1, -1, {}, [], 'hello' ], F: [ 0, '', undefined, null ] }
#10. flatten: 配列をきれいに
Qiitaで独自実装している人をよくみる。個人的には使ったことがない。
R.flatten( [1, [2,3], [4,5,[6]] ] ) // -> [1,2,3,4,5,6]