點燈坊

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

使用 map() + pick() 只取得 Object 部分 Property

Sam Xiao's Avatar 2020-07-10

RxJS 提供了 pluck() 直接取得 Object 的 Property,若要取得部分 Property,RxJS 並沒有提供直接 Operator,此時可使用 RxJS 的 map() + Ramda 的 pick() 實現。

Version

macOS Catalina 10.15.5
WebStorm 2020.1.2
Vue 2.6.11
RxJS 6.5.5
Ramda 0.27.0

Browser

pick000

按下 Get Book 僅顯示 titleprice

Data

{
  "id": 1,
  "title": "FP in JavaScript",
  "price": 100,
  "categoryId": 1,
  "image": "fpjs.jpg"
}

http://localhost:3000/books/:id 會回傳指定 id 的書籍。

可發現 API 回傳了眾多不需要的 property,顯示只需要 titleprice

map()

<template>
  <div>
    <button v-stream:click="{ subject: click$, data: 1 }">Get Book</button>
    <ul>
      <li v-for="(x, k) in book$" :key="k">{{ k }} : {{ x }}</li>
    </ul>
  </div>
</template>

<script>
import { ajax } from 'rxjs/ajax'
import { pluck, map, switchMap } from 'rxjs/operators'

let fetchBook$ = x => ajax(`http://localhost:3000/books/${x}`).pipe(
  pluck('response')
)

let subscriptions = function() {
  let book$ = this.click$.pipe(
    pluck('data'),
    switchMap(fetchBook$),
    map(x => ({ "title": x.title, "price": x.price }))
  )

  return { book$ }
}

export default {
  name:'App',
  domStreams: [
    'click$'
  ],
  subscriptions
}
</script>

19 行

let book$ = this.click$.pipe(
  pluck('data'),
  switchMap(fetchBook$),
  map(x => ({ "title": x.title, "price": x.price }))
)

可使用 map() 將所需 property 組合成 Object 回傳。

pick()

<template>
  <div>
    <button v-stream:click="{ subject: click$, data: 1 }">Get Book</button>
    <ul>
      <li v-for="(x, k) in book$" :key="k">{{ k }} : {{ x }}</li>
    </ul>
  </div>
</template>

<script>
import { ajax } from 'rxjs/ajax'
import { pluck, map, switchMap } from 'rxjs/operators'
import { pick } from 'ramda'

let fetchBook$ = x => ajax(`http://localhost:3000/books/${x}`).pipe(
  pluck('response')
)

let subscriptions = function() {
  let book$ = this.click$.pipe(
    pluck('data'),
    switchMap(fetchBook$),
    map(pick(['title', 'price'])),
  )

  return { book$ }
}

export default {
  name:'App',
  domStreams: [
    'click$'
  ],
  subscriptions
}
</script>

20 行

let book$ = this.click$.pipe(
  pluck('data'),
  switchMap(fetchBook$),
  map(pick(['title', 'price'])),
)

若要使 map() 的 callback 也 point-free,可使用 Ramda 的 pick(),將所需的 property 以 Array 傳入。

pick()
[k] -> {k: v} -> {k: v}
從一個 Object 中抽取部分 property 成為新 Object

[k]:要抽取的 property

{k: v}:data 為 object

{k: v} : 回傳新的 object

Conclusion

  • Ramda 對於 Object 與 Array 的支援非常完整,RxJS 的 operator 大多只為了 Observable 本身設計,若要在 callback 中操作 Object 與 Array,別忘了 Ramda 這個神兵利器

Reference

RxJS, map()
Ramda, pick()