點燈坊

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

使用 mix() 一次寫入多個 Property

Sam Xiao's Avatar 2020-03-27

若想一次寫入多個 Property 且 Immutable,ECMAScript 的 Property Shorthand 與 Ramda 的 assoc()set() 都有其極限,Wink-fp 的 mix() 可讓我們一次滿足兩個願望。

Version

macOS Catalina 10.15.4
VS Code 1.43.2
Quokka 1.0.284
ECMAScript 2015
Ramda 0.27.0
Wink-fp 1.20.60

ECMAScript

let obj = {
  price: 100
}

let title = 'Speaking JavaScript'
let price = 200

let result = { ...obj, title, price } // ? 

obj 原有 price property,我們希望以原來 object 為基礎修改 price property 並新增 title property,並且原來 object 維持 immutable。

ES6 可使用 object spread 展開原 object,並使用 property shorthand 一次寫入多個 property。

這一切看似美好,唯 ES6 為了效能考慮,... 為 shallow copy,在某些狀況可能不太適用。

mix000

Ramda

assoc()

import { assoc } from 'ramda'

let obj = {
  price: 100
}

let result = assoc('title')('Speaking JavaScript')(obj) // ?

assoc() 雖為 immutable,但一次只能寫入一個 property 也不方便。

mix001

set()

import { lensProp, set } from 'ramda'

let obj = {}

let titleLens = lensProp('title')

let result = set(titleLens)('Speaking JavaScript')(obj) // ?

set() 雖為 immutable,但一次只能寫入一個 property 也不方便。

mix002

mix()

import { pipe, clone, mergeDeepRight } from 'ramda'

let mix = pipe(
  clone,
  mergeDeepRight
)

let obj = {
  price: 100
}

let title = 'Speaking JavaScript'
let price = 200

let result = mix(obj)({ title, price }) // ?

第 3 行

let mix = pipe(
  clone,
  mergeDeepRight
)

第一個 argument 為原 object,使用 Ramda 的 clone() 做 deep copy,並將結果傳給 mergeDeepRight() 的第一個 argument。

第二個 argument 則使用 property shorthand 剛組合的 object,透過 mergeDeepRight() 也做 deep merge,並以右側 object 為主。

mix003

Wink-fp

import { mix } from 'wink-fp'

let obj = {
  price: 100
}

let title = 'Speaking JavaScript'
let price = 200

let result = mix(obj)({ title, price }) // ?

Wink-fp 已經內建 mix() 可直接使用。

mix()
{ a } -> { b } -> { c }
一次寫入多個 property 並建立全新 object

{ a }:原 object

{ b }:想一次寫入多個 property 的 object

{ c }:合併後全新 object

mix004

Conclusion

  • mix() 看似很神奇,其實只是組合了 clone()mergeDeepRight(),這就是 Function Composition 的魅力

Reference

Ramda, assoc()
Ramda, set()