點燈坊

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

使用 project() 只回傳 Array 中 Object 的部分 Property

Sam Xiao's Avatar 2020-04-11

map() + pick() 是實務上常用組合,可重構成 project() 更為優雅。

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: 'RxJS in Action', price: 200, category: 'FRP' },
  { title: 'Speaking JavaScript', price: 300, category: 'JS' }
]

let f = a => {
  let result = []

  for (let x of a)
    result.push({ title: x.title, price: x.price })

  return result
}

f(data) // ?

第 1 行

let data = [
  { title: 'FP in JavaScript', price: 100, category: 'FP' },
  { title: 'RxJS in Action', price: 200, category: 'FRP' },
  { title: 'Speaking JavaScript', price: 300, category: 'JS' }
]

我們只想擷取 titleprice 兩個 property 的 array。

第 7 行

let f = a => {
  let result = []

  for (let x of a)
    result.push({ title: x.title, price: x.price })

  return result
}

Imperative 會先建立 result empty array,使用 for loop 對 data 一筆一筆處理,然後只將部分 property push 進 result,最後回傳 result array。

project000

reduce()

let data = [
  { title: 'FP in JavaScript', price: 100, category: 'FP' },
  { title: 'RxJS in Action', price: 200, category: 'FRP' },
  { title: 'Speaking JavaScript', price: 300, category: 'JS' }
]

let f = a => a.reduce((a, x) => ([
  ...a, 
  { title: x.title, price: x.price }
]), [])

f(data) // ?

只要能使用 for loop,就能使用 reduce() 改寫。

project001

map()

let data = [
  { title: 'FP in JavaScript', price: 100, category: 'FP' },
  { title: 'RxJS in Action', price: 200, category: 'FRP' },
  { title: 'Speaking JavaScript', price: 300, category: 'JS' },
]

let f = a => a.map(x => ({ title: x.title, price: x.price }))

f(data) // ?

ECMAScript 可使用 map() 只將 titleprice 兩 property 回傳。

project002

Ramda

import { map } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100, category: 'FP' },
  { title: 'RxJS in Action', price: 200, category: 'FRP' },
  { title: 'Speaking JavaScript', price: 300, category: 'JS' }
]  

let f = map(x => ({ title: x.title, price: x.price }))

f(data) // ?

可使用 Ramda 的 map() 使 f() 能 point-free。

project003

pick()

import { map, pick } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100, category: 'FP' },
  { title: 'RxJS in Action', price: 200, category: 'FRP' },
  { title: 'Speaking JavaScript', price: 300, category: 'JS' }
]

let f = map(pick(['title', 'price']))

f(data) // ?

可使用 Ramda 的 pick() 使 map() 的 callback 也 point-free。

project004

project()

import { project } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100, category: 'FP' },
  { title: 'RxJS in Action', price: 200, category: 'FRP' },
  { title: 'Speaking JavaScript', price: 300, category: 'JS' }
]  

let f = project(['title', 'price'])

f(data) // ?

Ramda 還另外提供了 project,相當於 map()pick() 組合。

project()
[k] → [{k: v}] → [{k: v}]
只回傳 array 中 object 的部分 property

[k]:部分 property 以 array 提供

[{k: v}]:data 為 object array

[{k: v}]:回傳部分 property 的 object array

project006

Conclusion

  • 一開始可能沒想到 project(),但看到 map() + pick() pattern 別忘了重構成 project()

Reference

Ramda, project()
Ramda, map()
Ramda, pick()