點燈坊

學而時習之,不亦悅乎

使用 chain() 取得 Nested Array 內所有 Element

Sam Xiao's Avatar 2019-08-07

實務上常常用到 chain(),只是一時看不出來而已,可先用已知 Higher Order Function 拆解,利用 FP 的 Associative Laws 特性組合成 chain()

Version

macOS Mojave 10.14.5
VS Code 1.35.0
Quokka 1.0.224
ECMAScript 2019
Ramda 0.26.1

pipe()

import { pipe, prop, map, pluck } from 'ramda';

let data = {
  books: [
    { title: 'Secrets of the JavaScript Ninja',
      authors: [
        { name: 'John Resig' },
        { name: 'Bear Bibeault' }
      ]
    },
    { title: 'RxJS in Action',
      authors: [
        { name: 'Paul P.Daniels' },
        { name: 'Luis Atencio' }
      ]
    }
  ]
};

let usrFn = pipe(
  prop('books'),
  map(prop('authors')),
  map(pluck('name'))
);

console.dir(usrFn(data));

data 為 nested array,想將所有書的 author 抓出來,只有一層 array。

[ 'John Resig', 
  'Bear Bibeault', 
  'Paul P.Daniels', 
  'Luis Atencio' ] 

20 行

let usrFn = pipe(
  prop('books'),
  map(prop('authors')),
  map(pluck('name'))
);

由於是多層 array,直覺會先用 prop('books') 抓到資料,再使用兩次 map()

但結果是兩層 array,跟我們預期的一層 array 不同。

unnest000

unnest()

import { pipe, prop, map, pluck, compose, unnest, flatten } from 'ramda';

let data = {
  books: [
    { title: 'Secrets of the JavaScript Ninja',
      authors: [
        { name: 'John Resig' },
        { name: 'Bear Bibeault' }
      ]
    },
    { title: 'RxJS in Action',
      authors: [
        { name: 'Paul P.Daniels' },
        { name: 'Luis Atencio' }
      ]
    }
  ]
};

let usrFn = pipe(
  prop('books'),
  map(prop('authors')),
  unnest,
  pluck('name')
);

console.dir(usrFn(data));

可先使用 unnest() 攤平一層 array,再使用 pluck(),如此只剩下一層 array,這正是我們要的結果。

unnest001

flatten()

import { pipe, prop, map, pluck, flatten } from 'ramda';

let data = {
  books: [
    { title: 'Secrets of the JavaScript Ninja',
      authors: [
        { name: 'John Resig' },
        { name: 'Bear Bibeault' }
      ]
    },
    { title: 'RxJS in Action',
      authors: [
        { name: 'Paul P.Daniels' },
        { name: 'Luis Atencio' }
      ]
    }
  ]
};

let usrFn = pipe(
  prop('books'),
  map(prop('authors')),
  flatten,
  pluck('name')
);

console.dir(usrFn(data));

也可以使用 flatten() 將 array 攤平。

unnest003

chain()

import { pipe, prop, pluck, chain } from 'ramda';

let data = {
  books: [
    { title: 'Secrets of the JavaScript Ninja',
      authors: [
        { name: 'John Resig' },
        { name: 'Bear Bibeault' }
      ]
    },
    { title: 'RxJS in Action',
      authors: [
        { name: 'Paul P.Daniels' },
        { name: 'Luis Atencio' }
      ]
    }
  ]
};

let usrFn = pipe(
  prop('books'),
  chain(prop('authors')),
  pluck('name')
);

console.dir(usrFn(data));

pipe(map, flatten) 相當於 chain(),可利用 FP 的 associative laws 使用 chain() 加以化簡。

chain()
(a → b) → [a] → [b]
相當於 flatten()map() 組合

(a -> b):相當於 map function

[a]:data 為 array

[b]:回傳攤平後的 array

unnest002

Conclusion

  • 只要是多層 array,使用 chain() 的機會就很高,若一時看不出來,可先使用 map(),發現多了一層,再使用 unnest()flatten(),當看到 pipe(map, unnest)pipe(map, flatten) 時,就會想到使用 chain() 替換

Reference

Ramda, pipe()
Ramda, prop()
Ramda, map()
Ramda, pluck()
Ramda, compose()
Ramda, unnest()
Ramda, flatten()
Ramda, chain()