若 object 存在則 update,不存在就 insert
是實務上常見處理資料方式,Ramda 並沒有如 SQL 提供簡單寫法,但可以自行組合 Function。
Version
macOS Catalina 10.15.3
VS Code 1.42.1
Quokka 1.0.277
Ramda 0.27.0
ifElse()
import { prepend, findIndex, update, ifElse, any, pipe, __ } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = (obj, arr) => ifElse(
any(x => x.title === obj.title),
pipe(findIndex(x => x.title === obj.title), update(__, obj, arr)),
prepend(obj)
)(arr)
f({ title: 'Learning Haskell', price: 500 }, data) // ?
f({ title: 'Speaking JavaScript', price: 400 }, data) // ?
第 3 行
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
data
為 array of object,但後端會傳回只有 title
與 price
的 object,若 title
相同則視為相同,更新 price
即可,若 title
不同則視為不同,將新增 object 至最前面。
第 9 行
let f = (obj, arr) => ifElse(
any(x => x.title === obj.title),
pipe(findIndex(x => x.title === obj.title), update(__, obj, arr)),
prepend(obj)
)(arr)
使用 ifElse()
判斷,若資料存在則 update()
,若不存在則 prepend()
。
- 判斷要使用
any()
而不能使用includes()
,因為title
相同則視為相同 update()
需要 index,因此要先使用findIndex()
取得 index- 若找不到則直接
prepend()
沒問題
eqProps()
import { prepend, findIndex, update, ifElse, any, pipe, __, eqProps } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = (obj, arr) => ifElse(
any(eqProps('title', obj)),
pipe(findIndex(eqProps('title', obj)), update(__, obj, arr)),
prepend(obj)
)(arr)
f({ title: 'Learning Haskell', price: 500 }, data) // ?
f({ title: 'Speaking JavaScript', price: 400 }, data) // ?
any()
與 findIndex()
的 callback 可使用 eqProps()
使其 point-free。
edit()
import { prepend, findIndex, update, ifElse, any, pipe, __, eqProps } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let edit = obj => pipe(
findIndex(eqProps('title', obj)),
update(__, obj)
)
let f = (obj, arr) => ifElse(
any(eqProps('title', obj)),
edit(obj)(arr),
prepend(obj)
)(arr)
f({ title: 'Learning Haskell', price: 500 }, data) // ?
f({ title: 'Speaking JavaScript', price: 400 }, data) // ?
可抽出 edit()
,如此 ifElse()
可讀性更高。
Conclusion
- 本例並沒有徹底 point-free,主要是為了維持
ifElse()
架構,FP 應先追求可讀性高,其次才是追求 point-free,切勿為了 point-free 讓可讀性變低,這就不是 FP 目的了
Reference
Ramda, ifElse()
Ramda, prepend()
Ramda, findIndex()
Ramda, update()
Ramda, any()
Ramda, pipe()
Ramda, __()
Ramda, eqProps()
Ramda, pipe()