Ramda 有 any()
並不令人訝異,ECMAScript 也有 some()
,但 Ramda 又另外提供了 anyPass()
,這與 any()
有什麼不同呢 ?
Version
macOS Mojave 10.14.5
VS Code 1.33.0
Quokka 1.0.205
Ramda 0.26.1
Imperative
let x = undefined
let y = null
let z = ''
let f = x => {
if (x === undefined || x === null || x === '')
return true
else
return false
}
f(x) // ?
f(y) // ?
f(z) // ?
若要判斷 undefined
、null
或 empty string,ECMAScript 可使用 if
一一判斷並搭配 ||
。
Ramda
import { isNil, isEmpty } from 'ramda'
let x = undefined
let y = null
let z = ''
let f = x => isNil(x) || isEmpty(x)
f(x) // ?
f(y) // ?
f(z) // ?
Ramda 提供了 isNill()
判斷 undefined
或 null
,isEmpty()
判斷 empty string,也可搭配 ||
判斷。
anyPass()
import { isNil, isEmpty, anyPass } from 'ramda'
let x = undefined
let y = null
let z = ''
let f = anyPass([isNil, isEmpty])
f(x) // ?
f(y) // ?
f(z) // ?
更好的方式是改用 anyPass()
取代 ||
,如此在語義上可明確看出 isNil()
或 isEmpy()
只要 anyPass()
即可。
anyPass()
[(*… → Boolean)] → (*… → Boolean)
將眾多 predicate 組合成單一 predicate,只要任何 predicate 成立即可
[(*… → Boolean)]
:眾多 predicate 組合在 array 中
(*… → Boolean)
:組合成單一 predicate
Imperative
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = arr => {
let result = []
for (let x of arr)
if (x.price === 100 || x.price === 200)
result.push(x)
return result
}
f(data) // ?
第 1 行
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
只要是 price
為 100
或 200
都屬於合理價錢。
第 7 行
let f = arr => {
let result = []
for (let x of arr)
if (x.price === 100 || x.price === 200)
result.push(x)
return result
}
Imperative 會先建立 result
empty array,使用 for
loop 對 data 一筆一筆處理,然後使用 if
判斷 price
是否為 100
或 200
,最後 push 進 result
array 回傳。
ECMAScript
reduce()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = arr => arr.reduce((a, x) => (
(x.price === 100 || x.price === 200) ?
[...a, x] :
a
), [])
f(data) // ?
只要能使用 for
loop,就能使用 reduce()
改寫。
filter()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = arr => arr.filter(x => x.price === 100 || x.price === 200)
f(data) // ?
ECMAScript 可使用自帶的 filter()
,只要在 predicate 判斷 price
為 100
或 200
即可。
Ramda
import { filter } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = filter(x => x.price === 100 || x.price === 200)
f(data) // ?
也可使用 Ramda 的 filter()
使 f()
能 point-free。
import { anyPass, propEq, filter } from 'ramda';
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
];
let f = filter(anyPass([
propEq('price', 100),
propEq('price', 200)
]))
f(data) // ?
以 FP 角度,我們必須提供兩個 predicate,但只要其中一個 predicate 成立即可,因此使用 Ramda 的 anyPass()
將兩個使用 propEq()
產生的 predicate 組合成單一 predicate。
注意新 predicate 接受的 data 與原本 predicate 都相同,一樣是 object。
Conclusion
any()
結果是 boolean;而anyPass()
結果是 predicateany()
用於 array;而anyPass()
則用在組合多 predicate 成單一 predate,適合給filter()
這類 function 使用