將眾多 Predicate 以 Object 形式整理,產生 && 串起眾多 Predicate

filter()find() 這類 Operator,需要傳進 Predicate Function,若條件不只一個,就必須靠 && 串起來,造成程式碼不容易理解。Ramda 特別提供了 where(),專門負責產生這類 Predicate。

Version


VS Code 1.31.1
Quokka 1.0.136
Ramda 0.26.1

filter()


1
2
3
4
5
6
7
8
9
10
11
12
import { filter } from 'ramda';

const data = [
{ id: 1, title: 'Impatient JavaScript', year: 2018 },
{ id: 2, title: 'RxJS in Action', year: 2017 },
{ id: 3, title: 'Speaking JavaScript', year: 2014 }
];

const getBooks = filter(x => x.title.includes('JavaScript') && x.year >= 2017);

const result = getBooks(data);
console.dir(result);

想要找出 title 包含 JavaScriptyear 大於 2017 的 data

第 9 行

1
const getBooks = filter(x => x.title.includes('JavaScript') && x.year >= 2017);

使用了 Ramda 的 filter() 並搭配 predicate function,由於包含了兩個條件,因此必須使用 && 才能串起來。

where000

where()


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

const data = [
{ id: 1, title: 'Impatient JavaScript', year: 2018 },
{ id: 2, title: 'RxJS in Action', year: 2017 },
{ id: 3, title: 'Speaking JavaScript', year: 2014 }
];

const predicate = where({
title: includes('JavaScript'),
year: gte(__, 2017)
});

const getBooks = filter(predicate);

const result = getBooks(data);
console.dir(result);

filter() 的 predicate 改由 Ramda 的 where() 產生。

where()
{String: (* → Boolean)} → {String: *} → Boolean
將眾多 predicate 以 object 形式整理,產生 && 串起眾多 predicate

{String: (* → Boolean)}String 為 property 的 key,表示該 predicate 針對此 property;(* → Boolean) 為 predicate

{String: *}:data 為 object

Boolean:若符合 predicate 則傳回 true,否則傳回 false

由於 where() 的 currying 特性,只要提供第一個參數,就會產生 {String: *} → Boolean 的 predicate 供 filter()find() … 等 operator 使用

第 9 行

1
2
3
4
const predicate = where({
title: includes('JavaScript'),
year: gte(__, 2017)
});

另外定義 predicate(),由 where() 產生 function。

1
x => x.title.includes('JavaScript')

由於與 title 有關,所以 key 為 titleincludes() 則改用 Ramda 的 includes(),才能回傳 function。

includes()
a -> [a] -> Boolean

若 value 存在於 array 內時,則傳回 true,否則傳回 false

a:要測試的 value

[a]:data 為 array

Boolean:若存在則傳回 true,否則傳回 false

由於 includes() 的 currying 特性,只要提供第一個參數 value,就會產生 [a] -> Boolean 的 function,剛好適合給 where() 提供 function

1
x.year >= 2017

由於與 year 有關,所以 key 為 year>= 則改用 Ramda 的 gte(),才能回傳 function。

gte()
Ord a => a → a → Boolean
若第一個參數大於第二個參數則傳回 true,否則傳回 false

若寫成 gte(2017),則 year 將會放在第二個參數,就無法使用 gte(),必須改用 lte(),如此語意又與原本寫法不同,因此特別使用 gte()__(),將 year 改放在第一個參數。

__()
參數的 placeholder

我們可以發現原本由 && 所組成的 predicate,改由 where() 之後,由於其 Point-free 特性,predicate() 只有包含邏輯部分,而沒有任何 data,變得非常清爽,可讀性更高。

14 行

1
const getBooks = filter(predicate);

由於 filter() 的 predicate 已經被抽成 function,改由 where() 產生。

where001

Conclusion


  • where() 特別適合用來產生 predicate function 給 filter()find() 之類的 operator 使用

Reference


Ramda, where()
Ramda, filter()
Ramda, includes()
Ramda, gte()
Ramda, __()