點燈坊

失くすものさえない今が強くなるチャンスよ

使用 ifElse() 組合 prepend() 與 update()

Sam Xiao's Avatar 2020-03-02

若 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,但後端會傳回只有 titleprice 的 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() 沒問題

prepend000

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。

prepend001

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() 可讀性更高。

prepend002

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()