filter()
亦是 FP 常用的 Higher-order Function,事實上我們也可自行實作 filter()
練習 FP 基本功。
Version
macOS Catalina 10.15.3
VS Code 1.42.1
Quokka 1.0.289
Ramda 0.27.0
Imperative
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let filter = f => arr => {
let result = []
for(let x of arr)
if (f(x))
result.push(x)
return result
}
let f = v => arr => filter(x => x.price === v)(arr)
f(100)(data) // ?
第 1 行
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
我們只想過濾出 price
為 100
的資料。
第 7 行
let filter = f => arr => {
let result = []
for(let x of arr)
if (f(x))
result.push(x)
return result
}
Imperative 會先建立 result
empty array,使用 for
loop 對 data 一筆一筆處理,當 f(x)
結果為 true
時,將 x
push 進 result
array 回傳。
ECMAScript
reduce()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let filter = f => arr => arr.reduce((a, x) => f(x) ? [...a, x] : a, [])
let f = v = arr => filter(x => x.price === v)(arr)
filter(x => x.price === 100)(data) // ?
只要能使用 for
loop,就能使用 reduce()
改寫。
filter()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = v => arr => arr.filter(x => x.price === v)
f(100)(data) // ?
ECMAScript 已經內建 filter()
,可直接使用。
Ramda
import { filter } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = v => filter(x => x.price === v)
f(100)(data) // ?
Ramda 亦提供 filter()
,與內建不同的是 data 放在最後一個 argument,方便 function composition。
filter()
(a -> Boolean) -> [a] -> [a]
從 Array 過濾出想要的 Element
(a -> Boolean)
:過濾條件 predicate
[a]
:data 為 array
[a]
:回傳為過濾過 array
Point-free
propEq()
import { filter, propEq } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = v => filter(propEq('price', v))
f(100)(data) // ?
filter()
的 predicate 可由 propEq()
產生使 predicate 能 point-free。
import { filter, propEq, compose } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = compose(filter, propEq('price'))
f(100)(data) // ?
也可使用 compose()
組合 filter()
與 propEq()
使 f()
能 point-free。
where()
import { filter, where, equals } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = v => filter(where({
price: equals(v)
}))
f(100)(data) // ?
filter()
的 predicate 另外一種用法是搭配 where()
,尤其對 data 是 array of object 時非常好用。
import { filter, where, equals, compose, objOf } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = compose(filter, where, objOf('price'), equals)
f(100)(data) // ?
若要使 f()
也 point-free,可使用 compose()
從內層拆起,組合 filter()
、where()
、objOf()
與 eqauls()
。
Conclusion
filter()
雖然用起來很直覺,也可使用 imperative 與reduce()
實作,事實上 imperative 要重構時,與自行實作filter()
思考過程類似- 當 predicate 也 point-free 後,就更能看出 function composition 雛形,可使用
compose()
將filter()
與 callback 組合起來 - 當要使用
compose()
時,只要從內側一步一步向外拆解 function,就能順利寫出compose()
Reference
Ramda, filter()
Ramda, propEq()
Ramda, filter()
Ramda, compose()
Ramda, where()
Ramda, equals()
Ramda, objOf()