點燈坊

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

如何將 Nested Object Array 轉成 String ?

Sam Xiao's Avatar 2021-04-24

實務上常遇到 API 回傳資料為 Nested Object Array,但卻要以 , 隔開的 String 顯示,這種常見需求該如何實現呢 ?

Version

Ramda 0.27.1

map()

import { pipe, map } from 'ramda'

let data = [
  { 
    title: 'FP in JavaScript',    
    authors: [
      { name: 'John'},
      { name: 'Kevin'},
      { name: 'Tom'}
    ] },
  { 
    title: 'RxJS in Action',      
    authors: [
      { name: 'Sam' },
      { name: 'Jerry' },
      { name: 'Ray' }
    ] 
  },
  { 
    title: 'Speaking JavaScript', 
    authors: [
      { name: 'May'},
      { name: 'Mary'},
      { name: 'Jen'}
    ] 
  }
]

pipe(
  map(x => ({
    title: x.title,
    authors: x.authors.map(x => x.name)
  })),
  map(x => ({
    title: x.title,
    authors: x.authors.join(',')

  }))
)(data) // ?

第 3 行

let data = [
  { 
    title: 'FP in JavaScript',    
    authors: [
      { name: 'John'},
      { name: 'Kevin'},
      { name: 'Tom'}
    ] },
  { 
    title: 'RxJS in Action',      
    authors: [
      { name: 'Sam' },
      { name: 'Jerry' },
      { name: 'Ray' }
    ] 
  },
  { 
    title: 'Speaking JavaScript', 
    authors: [
      { name: 'May'},
      { name: 'Mary'},
      { name: 'Jen'}
    ] 
  }
]

authors 是以 Object Array 型態存在,但卻希望以 String 顯示。

分兩階段處理:

  • 先將 Object Array 轉成 Array
  • 再將 Array 轉成 String

30 行

map(x => ({
  title: x.title,
  authors: x.authors.map(x => x.name)
})),
  • map():在 map() 中再使用 Array.prototype.map() 取出 Object Array 中的 name 成為單純 Array。

34 行

map(x => ({
  title: x.title,
  authors: x.authors.join(',')
}))
  • map():新資料筆數與原筆數相同,因此使用 map()
  • join():回傳仍是 Object,因此使用 arrow function 回傳 Object,authors Array 使用 Array.prototype.join(',') 轉成 String

join000

Point-free

import { pipe, map, lensProp, over, join, pluck } from 'ramda'

let data = [
  { 
    title: 'FP in JavaScript',    
    authors: [
      { name: 'John'},
      { name: 'Kevin'},
      { name: 'Tom'}
    ] },
  { 
    title: 'RxJS in Action',      
    authors: [
      { name: 'Sam' },
      { name: 'Jerry' },
      { name: 'Ray' }
    ] 
  },
  { 
    title: 'Speaking JavaScript', 
    authors: [
      { name: 'May'},
      { name: 'Mary'},
      { name: 'Jen'}
    ] 
  }
]

pipe(
  map(over(lensProp('authors'), pluck('name'))),
  map(over(lensProp('authors'), join(',')))
)(data) // ?

30 行

map(over(lensProp('authors'), pluck('name')))
  • pluck('name'):先從 Object Array 取出 name 寫入 authors property 使之成單純 Array

亦可使用 map(prop('name')),但 pluck('name') 較精簡

31 行

map(over(lensProp('authors'), join(',')))

整個關鍵在於 join(',')authors Array 轉成 String,但 join(',') 是 function 而非 value,有幾個 function 可考慮:

  • assoc():可直接修改 authors Array,但必須提供 value,因此並不適合
  • set():可直接修改 authors Array,但必須提供 value,因此並不適合
  • over():可直接修改 authors Array,但必須提供 function,因此可直接使用 join(',')

over() 需搭配 Lens,因此使用 lensProp('authors') 先建立 Lens,如此 map() 的 callback 可完全 Point-free。

join000

Conclusion

  • Ramda 有時很難一步完成,會先使用 map() 達成階段性任務之後,再使用另外一個 map() 完成

Reference

Ramda, join()
Ramda, over()