Ramda 有 all()
並不令人訝異,ECMAScript 也有 every()
,但 Ramda 又另外提供了 allPass()
,這與 all()
有什麼不同呢 ?
Version
macOS Mojave 10.14.5
VS Code 1.33.0
Quokka 1.0.205
Ramda 0.26.1
Imperative
let x = 8
let y = 20
let z = 17
let f = x => {
if (x > 10 && (x % 2 === 0))
return true
else
return false
}
f(x) // ?
f(y) // ?
f(z) // ?
若要判斷 大於 10
且為 偶數
,ECMAScript 可使用 if
一一判斷並搭配 &&
。
let x = 8
let y = 20
let z = 17
let isGt10 = x => x > 10
let isEven = x => !(x % 2)
let f = x => isGt10(x) && isEven(x)
f(x) // ?
f(y) // ?
f(z) // ?
也可將判斷 expression 抽成 isGt10()
與 isEven()
,再搭配 &&
判斷。
Ramda
import { allPass } from 'ramda'
let x = 8
let y = 20
let z = 17
let isGt10 = x => x > 10
let isEven = x => !(x % 2)
let f = allPass([isGt10, isEven])
f(x) // ?
f(y) // ?
f(z) // ?
Ramda 則可使用 allPass()
取代 &&
。
allPass()
[(*… → 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.title.includes('JavaScript'))
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
且 title
包含 JavaScript
的資料。
第 7 行
let f = arr => {
let result = []
for (let x of arr)
if (x.price >= 100 && x.title.includes('JavaScript'))
result.push(x)
return result
}
Imperative 會先建立 result
empty array,使用 for
loop 對 data 一筆一筆處理,若 price
大於 100
且 title
包含 JavaScript
則 push 進 result
,最後回傳 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.title.includes('JavaScript')) ?
[...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.title.includes('JavaScript'))
f(data) // ?
ECMAScript 可使用 filter()
並傳入判斷 price
與 title
的 predicate。
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.title.includes('JavaScript'))
f(data) // ?
也可使用 Ramda 的 filter()
使 f()
point-free。
import { allPass, propSatisfies, gte, __, includes, 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(allPass([
propSatisfies(gte(__, 100), 'price'),
propSatisfies(includes('JavaScript'), 'title')
]))
f(data) // ?
以 FP 角度,我們必須提供兩個 predicate,但必須兩個 predicate 都成立,因此使用 Ramda 的 allPass()
將兩個 predicate 組合成單一 predicate。
注意新 predicate 接受的 data 與原本 predicate 都相同,一樣是 object。
Conclusion
all()
結果是 boolean;而allPass()
結果是 predicateall()
用於 array;而allPass()
則用在組合多 predicate 成單一 predate,適合給filter()
這類 function 使用
Reference
Ramda, allPass()
Ramda, all()
Ramda, includes()
Ramda, lte()
Ramda, propSatisfies()