LoginSignup
2
2

More than 5 years have passed since last update.

Ramda 利用時の注意事項

Last updated at Posted at 2018-08-21

■ range には step がない

lodash には step があるのに:

> R.range(1, 5, 2)
[ 1, 2, 3, 4 ]

■ range はエコでない

要素数が無限なループはジェネレータを使う必要あり:

> // never do this: const input = R.range(0, Infinity)
> let input = (function* () { for(let i = 0; ; i += 1) yield i }());
> R.reduceWhile((acc, x) => x < 5, R.flip(R.append), [], input)
[ 0, 1, 2, 3, 4 ]

Immutable.js の Range を使うのがよい:

> const { Range } = require('immutable')
> input = Range(0, Infinity);
> R.reduceWhile((acc, x) => x < 5, R.flip(R.append), [], input)
[ 0, 1, 2, 3, 4 ]

■ flatten より unnest

flatten は強力すぎる。多くの場合、unnest の方が適切:

> R.unnest(R.range(0,3).map(x => R.range(x,3).map(y => [x, y])))
[ [ 0, 0 ], [ 0, 1 ], [ 0, 2 ], [ 1, 1 ], [ 1, 2 ], [ 2, 2 ] ]
> R.flatten(R.range(0,3).map(x => R.range(x,3).map(y => [x, y])))
[ 0, 0, 0, 1, 0, 2, 1, 1, 1, 2, 2, 2 ]

■ unary を使え

これは Ramda の問題というよりも、JS の問題。parseInt, console.log, fetch などなど第二引数を受け取ることで挙動が変わる関数を、Array.prototype.map に渡すと意図しない結果になる:

> ['1', '2', '3'].map(parseInt)
[ 1, NaN, NaN ]
> ['1', '2', '3'].map(R.unary(parseInt))
[ 1, 2, 3 ]
> R.map(parseInt, ['1', '2', '3']) // R.map ならOK
[ 1, 2, 3 ]

■ reduceRight に気を付けろ

Array.prototype.reduceRight の reducer と引数が逆!!!!

> [1, 2, 3].reduceRight((acc, x) => x)
1
> R.reduceRight((x, acc) => x, 'init', [1, 2, 3]) // OK
1
> R.reduceRight((acc, x) => x, 'init', [1, 2, 3]) // NG
'init'

しかも R.reduced で打ち切れない

> R.reduceRight(()=>R.reduced(100), 0, R.range(20))
{ '@@transducer/value': 100, '@@transducer/reduced': true }

■ split と負の引数は相性が悪い

配列の最後の 3 要素を取り出したいときは、若干トリッキー。むしろ使うのを避けた方がいい(takeLast を使う):

> [1, 2, 3, 4, 5].slice(-3)
[ 3, 4, 5 ]
> R.slice(-3, Infinity, [1, 2, 3, 4, 5])
[ 3, 4, 5 ]
> R.takeLast(3, [1, 2, 3, 4, 5])
[ 3, 4, 5 ]

■ repeat には癖がある

repeat 対象は同一オブジェクトを参照している

> arrs = R.repeat([1, 2, 3], 4)
[ [ 1, 2, 3 ], [ 1, 2, 3 ], [ 1, 2, 3 ], [ 1, 2, 3 ] ]
> arrs[0].pop()
3
> arrs
[ [ 1, 2 ], [ 1, 2 ], [ 1, 2 ], [ 1, 2 ] ]

■ 似ている関数に気を付ける

  • allallPass
> R.all(x => x % 2 === 0)([2, 4, 6]) // Array.prototype.every と同等
true
> R.allPass([x => x > 0, x => x % 2 === 0])(2)
true
  • sumadd: 適材適所。同様に、productmultiply も気を付ける。
> R.sum([1, 2, 3])
6
> R.reduce(R.add, 0, [1, 2, 3])
6
  • notcomplement: complementを使うべきところを not にしがち(私だけ?)
> R.not(false)
true
> R.complement(R.isEmpty)([1])
true
  • invertinvertObj: invertObj の方が使う機会が多い
> R.invert({ one: 1, two: 2 })
{ '1': [ 'one' ], '2': [ 'two' ] }
> R.invertObj({ one: 1, two: 2 })
{ '1': 'one', '2': 'two' }
  • headinit: 同様に lasttail
> R.head([1, 2, 3])
1
> R.init([1, 2, 3])
[ 1, 2 ]
> R.last([1, 2, 3])
3
> R.tail([1, 2, 3])
[ 2, 3 ]
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2