若 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
才能抓到值。
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。
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()
。
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
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()
。
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()