自行組合 Ramda 所沒提供的 Function

Ramda 有提供 propEq(),特別適合為 Object 提供 Predicate,但可惜只能用在 equals(),但若要配合 gt()gte()lt()lte() 時,我們能打造類似 propEq()propGte() 嗎 ?

Version


VS Code 1.33.0
Quokka 1.0.205
Ramda 0.26.1

propGte()


pipe()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { pipe, prop, gte, __ } from 'ramda';

let propGte = (name, val) => pipe(
prop(name),
gte(__, val)
);

let obj = {
name: 'Sam',
age: 20
};

let isAdult = propGte('age', 18);
console.log(isAdult(obj));

propGte()propEq() 一樣,傳入 nameval 參數,最基本的想法就是直接組合 prop()gte()

propgte000

若不求 Point-free,這已經是很棒的解法,充分利用了 FP 的 Function Composition 組合出 propGte(),但有可能進一步 Point-free 嗎 ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { pipe, prop, gte, flip } from 'ramda';

let propGte = (name, val) => pipe(
prop(name),
flip(gte)(val)
);

let obj = {
name: 'Sam',
age: 20
};

let isAdult = propGte('age', 18);
console.log(isAdult(obj));

val 因為傳入到 gte() 第一個參數,所以特別使用 flip()gte() 參數做對調,使之成為 FP 風格的最後一個參數方便 Point-free。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { pipe, prop, gte, flip, useWith } from 'ramda';

// propGte :: String -> Number -> Boolean
let propGte = useWith(
pipe, [
prop,
flip(gte)
]
);

let obj = {
name: 'Sam',
age: 20
};

let isAdult = propGte('age', 18);
console.log(isAdult(obj));

我們可以發現 prop()flip(gte) 其實都是參數的 transformer function,最後都交給 pipe() 執行,因此適合使用 useWith()

因為 useWith() 會將參數 Point-free,所以會特別加上型別註解方便日後維護。

propgte001

propSatisfies()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { gte, propSatisfies, __ } from 'ramda';

// propGte :: String -> Number -> Boolean
let propGte = (name, val) => propSatisfies(
gte(__, val),
name
);

let obj = {
name: 'Sam',
age: 20
};

let isAdult = propGte('age', 18);
console.log(isAdult(obj));

除了 propEq(),Ramda 另外提供了 propSatisfies(),也可以使用此 operator 組合出 propGte()

propSatisfies()
(a → Boolean) → String → {String: a} → Boolean
提供 predicate 與 property 名稱,回傳 object 的 callback

propgte002

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { gte, propSatisfies, flip } from 'ramda';

// propGte :: String -> Number -> Boolean
let propGte = (name, val) => propSatisfies(
flip(gte)(val),
name
);

let obj = {
name: 'Sam',
age: 20
};

let isAdult = propGte('age', 18);
console.log(isAdult(obj));

一樣使用 flip()gte() 參數做對調,使之成為 FP 風格的最後一個參數方便 Point-free。

propgte003

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { gte, propSatisfies, flip, useWith, identity } from 'ramda';

// propGte :: String -> Number -> Boolean
let propGte = useWith(
flip(propSatisfies), [
identity,
flip(gte)
]
);

let obj = {
name: 'Sam',
age: 20
};

let isAdult = propGte('age', 18);
console.log(isAdult(obj));

最終一樣要使用 useWith(),但 propSatisfies() 的參數與 propGte() signature 不合,再使用 flip() 對調一次。

name 部分不變,因此使用 identity()

propgte004

Conclusion


  • propGte() 可使用 pipe() 組合,也可使用 propStatisfies() 組合,Ramda 常常同一個需求有多種組合方式,就類似相同數學問題,常會有多種不同解法
  • 可將 propGte() 成為自己專屬的 Ramda function,將來只要關於 object 的 predicate,且不是 equals(),而是 gte() 時,就可使用自製的 propGte()
  • propGt()propLte()propLt() 也可使用本方法加以打造,請舉一反三