Ramda 有提供 propEq()
,特別適合為 Object 提供 Predicate,但可惜只能用在 equals()
,但若要配合 lt()
時,我們能打造類似 propEq()
的 propLt()
嗎 ?
Version
macOS Catelina 10.15.6
Wink-fp 1.23.2
Predicate
import { filter } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
// f :: a -> [Object] -> [Object]
let f = v => filter(x => x.price < v)
f(200)(data) // ?
第 9 行
// f :: a -> [Object] -> [Object]
let f = v => filter(x => x.price < v)
使用 filter()
找到指定 price < 200
的 Object,第一個 argument 為 predicate,因此可傳入 arrow function。
prop()
import { pipe, filter, prop, lt, flip } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
// f :: a -> [Object] -> [Object]
let f = v => filter(pipe(prop('price'), flip(lt)(v)))
f(200)(data) // ?
第 9 行
// f :: a -> [Object] -> [Object]
let f = v => filter(pipe(prop('price'), flip(lt)(v)))
對於這種 Object Array,且 predicate 非 等於
,要使 filter()
的 callback 能 point-free 有兩種方法,先談較直覺的 prop()
。
由於是 Object,直覺要先使用 prop()
取得 value,再使用 lt()
比較其值,因此採用 pipe()
將 prop()
與 lt()
組合產生 filter()
所需的 callback。
propSatisfies()
import { filter, propSatisfies, lt, flip } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
// f :: a -> [Object] -> [Object]
let f = v => filter(propSatisfies(flip(lt)(v), 'price'))
f(200)(data) // ?
第 9 行
// f :: a -> [Object] -> [Object]
let f = v => filter(propSatisfies(flip(lt)(v), 'price'))
第二個方法是採用 propSatisfies()
,只要不能採用 propEq()
,都可改用 propSatisfies()
達成。
propLt()
prop()
import { filter, prop, lt, flip, pipe, useWith } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
// propLt :: String -> a -> Object -> Boolean
let propLt = useWith(
pipe, [prop, flip(lt)]
)
// f :: a -> [Object] -> [Object]
let f = v => filter(propLt('price')(v))
f(200)(data) // ?
10 行
// propLt :: String -> a -> Object -> Boolean
let propLt = useWith(
pipe, [prop, flip(lt)]
)
若要模仿 propEq()
做出 propLt()
,也有兩種方式,先看叫直覺的 prop()
。
由於 argument 先提供 property,再提供 value,最後使用 pipe()
串起來,這剛好是 useWith()
格式。
propSatisfies()
import { filter, lt, propSatisfies, flip, useWith, identity } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
// propLt :: String -> a -> Object -> Boolean
let propLt = useWith(
flip(propSatisfies), [identity, flip(lt)]
)
// f :: a -> [Object] -> [Object]
let f = v => filter(propLt('price')(v))
f(200)(data) // ?
第 9 行
// propLt :: String -> a -> Object -> Boolean
let propLt = useWith(
flip(propSatisfies), [identity, flip(lt)]
)
也可以使用 propSatisfies()
,但因為其 argument 順序剛好與 propLt()
相反,因此先透過 flip()
翻轉。
Wink-fp
import { filter } from 'ramda'
import { propLt } from 'wink-fp'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
// f :: a -> [Object] -> [Object]
let f = v => filter(propLt('price')(v))
f(200)(data) // ?
Wink-fp 已經內建 propLt()
可直接使用。
propLt()
String -> a -> Object -> Boolean
String
:提供 Object 的 property
a
:提供要比較的值
Object
:data 為 Object
Boolean
:若成立回傳 true
,否則回傳 false
Function Pipeline
import { filter, pipe } from 'ramda'
import { propLt } from 'wink-fp'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
// f :: a -> [Object] -> [Object]
let f = pipe(
propLt('price'),
filter
)
f(200)(data) // ?
10 行
// f :: a -> [Object] -> [Object]
let f = pipe(
propLt('price'),
filter
)
有了 propLt()
除了語意更清楚外,f()
也能輕鬆加以 point-free。
Conclusion
propLt()
在實務上經常使用,但可惜 Ramda 沒有內建,只好自行組合並收錄在 Wink-fppropLt()
可使用prop()
組合lt()
,也可使用propStatisfies()
,Ramda 常常同一個需求有多種組合方式,就類似相同數學問題,常會有多種不同解法propGte()
、propGt()
與propLte()
也可使用本方法加以打造,請舉一反三
Reference
Ramda, filter()
Ramda, prop()
Ramda, lt()
Ramda, propSatisfies()
Ramda, useWith()