點燈坊

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

使用 eqProps() 同時 prop() 與 propEq()

Sam Xiao's Avatar 2019-10-27

若 Predicate 的 Argument 為 Object,我們需要對 Argument 先 Destructure 然後再判斷比較,此時可使用 Ramda 的 eqProps() 一步完成。

Version

macOS Catalina 10.15
VS Code 1.39.2
Quokka 1.0.258
ECMAScript 2015
Ramda 0.26.1

Predicate

import { filter } from 'ramda';

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

let fn = obj => filter(x => x.price === obj.price);

fn({ price: 100 })(data); // ?

第 9 行

let fn = obj => filter(x => x.price === obj.price);

使用 filter() 找到指定 price 的 object,第一個 argument 為 predicate,因此可傳入 arrow function。

由於 argument 為 object,因此必須使用 obj.price 才能抓到值。

eqprops000

Object Destructuring

import { filter } from 'ramda';

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

let fn = ({ price }) => filter(x => x.price === price);

fn({ price: 100 })(data); // ?

ES6 迎來了 object destructuring,可在 parameter 直接將 object destructure 為 variable。

eqprops001

Point-free

import { filter, compose, prop, useWith, identity, propEq } from 'ramda';

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

let fn = useWith(
  filter, [compose(propEq('price'), prop('price')), identity]
);


fn({ price: 100 })(data); // ?

若想將 fn() point-free,由於 filter() 為最終執行 function,argument 為 predicate 與 data,因此可使用 useWith() 加以 point-free。

其中 predicate 可使用 compose(propEq('price'), prop('price')) 加以組合,data 則為 identity()

eqprops002

Ramda

import { filter, useWith, identity, eqProps } from 'ramda';

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

let fn = useWith(
  filter, [eqProps('price'), identity]
);


fn({ price: 100 })(data); // ?

當 argument 為 object 時,compose(propEq('price'), prop('price')) 這種 pattern 經常出現,因此 Ramda 提供了 eqProps()

eqProps()
k → {k: v} → {k: v} → Boolean
根據指定的 property 比較兩個 object 是否相等

k:指定要比較 property 的 key

{k: v}:要比較的 source object

{k: v}:data 為要比較的 target object

Boolean:若相等傳回 true,不相等傳回 false

由於 eqProps() 的 currying 特性,只提供前兩個參數,剛好回傳 {k: v} -> Boolean ,特別適合產生 predicate

eqprops002

Function Composition

import { filter, compose, eqProps } from 'ramda';

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

let fn = compose(filter, eqProps('price'));

fn({ price: 100 })(data); // ?

useWith() + identity() 這種 pattern,其實就是 compose()

eqprops003

Conclusion

  • 當需要對 argument 做 destructure 時,可考慮使用 eqProps()
  • compose() 出現 propEq()prop() 針對同一個 property 時,可使用 eqProps() 重構
  • 當出現 useWith() + identity() 時,可再簡單重構成 compose()

Reference

Ramda, filter()
Ramda, propEq()
Ramda, eqProps()
Ramda, compose()
Ramda, prop()
Ramda, useWith()
Ramda, identity()