如何使用 chain() 取代 filter() + map() ?

filter()map() 都是 FP 代表性的 Higher Order Function,但也可使用 chain() 實踐。

Version

VS Code 1.33.0

Quokka 1.0.209

Ramda 0.26.1

map()

import { map } from 'ramda';

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

let getBooks = map(x => {
  if (x.price < 300)
    return `${x.title} is not expensive`;
});

console.dir(getBooks(data));

若我們希望在 map() 的 callback 內加上條件,若 price 小於 300 ,就顯示 is not expensive

由於 map() 的特性是原來 data 有幾筆,結果有幾筆,因此 price 大於等於 300 時,會出現 undefined ,這應該不是我們樂見的。

filter() + map()

import { pipe, map, filter } from 'ramda';

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

let getBooks = pipe(
  filter(x => x.price < 300),
  map(x => `${x.title} is not expensive`)
);  

console.dir(getBooks(data));

看到在 map() 的 callback 有 if ,我們會想重構成先使用 filter() 過濾後再去 map() ,這種寫法就不會有 undefined 了。

Point-free

import { pipe, map, filter, prop, gt, concat, __ } from 'ramda';

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

let getBooks = pipe(
  filter(pipe(prop('price'), gt(300))),
  map(pipe(prop('title'), concat(__, ' is not expensive')))
);  

console.dir(getBooks(data));

也可以將 filter()map() 內的 callback 加以 Point-free,但這並不是重點,可依可讀性決定要不要真的去 Point-free。

Point-free 的目的就是讓可讀性變高,但有時條件太複雜,反而會使 callback 更複雜,因此要視情況使用

chain()

import { chain } from 'ramda';

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

let getBooks = chain(x => {
    if (x.price < 300)
      return [`${x.title} is not expensive`];
    else
      return [];
  }
);  

console.dir(getBooks(data));

map() 改成 chain() ,且維持原本 callback 大架構不變。

return string 改成 return array of string

原本沒有 else 改成 return []

因為 chain() 就是 flatMap() ,因此 array of string 會變成 string,且 [] 會自動過濾,因此不會出現 undefined

Conclusion

  • chain() 會維持原本 map() callback 的大架構,只要稍微重構即可
  • filter() + map() 改寫幅度較大,觀念也不太一樣
  • filter() + map() 似乎不太需要 Point-free,因為可讀性並沒有變高
  • chain() 的寫法是否有比 filter() + map() 好比較見仁見智,但也不失為漂亮的寫法

Reference

Ran Bar-Zip , Using flatMap in ES2019

Ramda , chain()

Ramda , map()

Ramda , filter()

Ramda , pipe()

Ramda , prop()

Ramda , gt()

Ramda , concat()

Ramda , __()

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章