寫程式免不了要使用 if else
判斷,但 if else
是 Imperative 產物, Ramda 則提供 ifElse()
讓我們在 Function Pipeline 中使用。
Version
macOS Catalina 10.15.4
VS Code 1.44.0
Quokka 1.0.285
Ramda 0.27.0
Imperative
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'Rx in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS'}
]
let f = a => {
let result = []
for (let x of a) {
x_ = Object.assign({}, x)
if (x.category === 'FP')
x_.price = x.price * 0.8
else
x_.price = x.price * 0.9
result.push(x_)
}
return result
}
f(data) // ?
第 1 行
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'Rx in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS'}
]
若 category
為 FP
,其 price
將打 8 折,其餘打 9 折。
第 7 行
let f = discount1 => discount2 => arr => {
let result = []
for (let x of arr) {
x_ = Object.assign({}, x)
if (x.category === 'FP')
x_.price = x.price * 0.8
else
x_.price = x.price * 0.9
result.push(x_)
}
return result
}
Imperative 會先建立 result
empty array,使用 for
loop 對 data 一筆一筆處理,然後使用 Object.assign()
clone object,若 category
property 為 FP
,則 price
打 8 折,其餘打 9 折,最後 push 進 result
array 回傳。
reduce()
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'Rx in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS'}
]
let f = a => a.reduce((a, x) => (
(x.category === 'FP') ?
[...a, {...x, price: x.price * 0.8 }] :
[...a, {...x, price: x.price * 0.9 }]
), [])
f(data) // ?
只要能使用 for
loop,就能使用 reduce()
改寫。
map()
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'Rx in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS'}
]
let f = a => a.map(x => {
if (x.category === 'FP')
return {...x, price: x.price * 0.8 }
else
return {...x, price: x.price * 0.9 }
})
f(data) // ?
ECMAScript 比較好的方法是改用 map()
,並在其 callback 中使用 if else
判斷 category
是否為 FP
。
Ramda
import { map } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'Rx in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS'}
]
let f = map(x => {
if (x.category === 'FP')
return {...x, price: x.price * 0.8 }
else
return {...x, price: x.price * 0.9 }
})
f(data) // ?
也可使用 Ramda 的 map()
取代,可使 f()
能 point-free。
ifElse()
import { map, ifElse, propEq, assoc, identity, chain, prop, multiply, useWith } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'Rx in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS'}
]
let applyDiscount = useWith(
multiply, [identity, prop('price')]
)
let f = map(ifElse(
propEq('category')('FP'),
chain(assoc('price'), applyDiscount(0.8)),
chain(assoc('price'), applyDiscount(0.9))
))
f(data) // ?
Ramda 正規方式是使用 ifElse()
取代 if else
。
ifElse()
(*... -> boolean) -> (*...-> *) -> (*...-> *) -> (*... -> *)
根據條件回傳不同 function
(*... -> boolean)
:任意 argument 的 function,只要回傳 boolean 即可(*...-> *)
:任意 argument 的 function,回傳也不拘(*...-> *)
:任意 argument 的 function,回傳也不拘(*...-> *)
:若為 true
則回傳第二個 function,若為 false
則傳回第三個function
Lens
import { map, ifElse, propEq, identity, lensProp, over, multiply } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'Rx in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS'}
]
let priceLens = lensProp('price')
let f = map(ifElse(
propEq('category')('FP'),
over(priceLens, multiply(0.8)),
over(priceLens, multiply(0.9))
))
f(data) // ?
若要修改 object,透過 lens 也是不錯方式。
第 9 行
let priceLens = lensProp('price')
先使用 lensProp()
為 price
property 建立 lens。
13 行
over(priceLens, multiply(0.8)),
直接透過 over
修改 price
property,不必搭配 assoc()
、chain()
與 prop()
。
Conclusion
ifElse()
讓我們在 pipeline 也可以使用if else
判斷,卻不會中斷 pipeline- 若回傳為固定值,可使用
always()
產生 function - 若回傳為相同值,可使用
identity()
產生 function - 若要修改 object,改用 lens 是很不錯方式,可避免組合
prop()
與assoc()
的尷尬,直接使用over()
即可
Reference
Ramda, ifElse()
Ramda, map()
Ramda, lensProp()
Ramda, identity()