實務上較少更改 map()
所回傳的 object,若要新增 property 可使用 assoc()
。
Version
macOS Catalina 10.15.3
VS Code 1.42.1
Quokka 1.0.277
Ramda 0.27.0
Imperative
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = k => v => arr => {
let result = []
for(let x of arr) {
let obj = Object.assign({}, x)
obj[k] = v
result.push(obj)
}
return result
}
f('author')('John Doe')(data) // ?
Imperative 會使用 for
loop,先使用 Object.assign()
clone object,在搭配 []
以指定值對 object 新增 property,最後將 obj
push 進 result
array 回傳。
Imperative 會先建立 result
empty array,使用 for
loop 對 data 一筆一筆處理,然後使用 Object.assign()
clone object,在搭配 []
以指定值對 object 新增 property,最後將 obj
push 進 result
array 回傳。
ECMAScript
reduce()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = k => v => arr => arr.reduce((a, x) => [...a, {...x, [k]: v}], [])
f('author')('John Doe')(data) // ?
只要能使用 for
loop,就能使用 reduce()
改寫。
map()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = k => v => arr => arr.map(x => ({...x, [k]: v}))
f('author')('John Doe')(data) // ?
ECMAScript 可使用 map()
與 object spread 新增 property。
Ramda
import { map } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = k => v => map(x => ({...x, [k]: v}))
f('author')('John Doe')(data) // ?
可使用 Ramda 的 map()
使 arr
argument point-free。
assoc()
import { map, assoc } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = k => v => map(assoc(k)(v))
f('author')('John Doe')(data) // ?
也可使用 assoc()
新增 property 產生 map()
的 callback。
compose()
import { map, assoc, compose } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = k => v => compose(
map,
assoc(k)
)(v)
f('author')('John Doe')(data) // ?
由於 assoc()
是 map()
的 callback,因此可用 compose()
將 assoc()
與 map()
組合,最後利用 IIFE 傳入 v
求得結果。
Point-free
import { map, assoc, compose } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = k => compose(
map,
assoc(k)
)
f('author')('John Doe')(data) // ?
由於 v
為最後一個 argument,剛好可 point-free 消除,但可惜還留下 k
。
useWith()
import { map, assoc, compose, identity, useWith, curry } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = curry(compose(
map,
useWith(assoc, [identity, identity])
))
f('author')('John Doe')(data) // ?
也有另外一種 point-free 方法,由於 assoc()
要接受 k
與 v
兩個 argument,可使用 useWith()
與 identity()
墊出兩個 argument,如此則完全 point-free。
useWith()
雖然支援 curried function,但使用 compose()
時會當成 unary function 組合,因此最後結果不是 curried function,需再使用 curry()
使其 currying。
Conclusion
- Ramda 會使用
assoc()
新增 property,一般而言重構成compose()
就很不錯,若能力許可可用useWith()
進一步 point-free