■ 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 ] ]
■ 似ている関数に気を付ける
-
all
とallPass
> R.all(x => x % 2 === 0)([2, 4, 6]) // Array.prototype.every と同等
true
> R.allPass([x => x > 0, x => x % 2 === 0])(2)
true
-
sum
とadd
: 適材適所。同様に、product
とmultiply
も気を付ける。
> R.sum([1, 2, 3])
6
> R.reduce(R.add, 0, [1, 2, 3])
6
-
not
とcomplement
:complement
を使うべきところをnot
にしがち(私だけ?)
> R.not(false)
true
> R.complement(R.isEmpty)([1])
true
-
invert
とinvertObj
:invertObj
の方が使う機会が多い
> R.invert({ one: 1, two: 2 })
{ '1': [ 'one' ], '2': [ 'two' ] }
> R.invertObj({ one: 1, two: 2 })
{ '1': 'one', '2': 'two' }
-
head
とinit
: 同様にlast
とtail
> 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 ]