ECMAScript 提供了 splice()
刪除 Array 中的 Element,但必須先提供要刪除的 Index
;但若要刪除的是 Object,由於 Object 的比較是 Reference,所以實踐方式比較不一樣。
Version
macOS Catalina 10.15.1
VS Code 1.40.2
Quokka 1.0.262
ECMAScript 2015
Array.prototype.indexOf()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
];
let obj = { title: 'RxJS in Action', price: 200 }
let fn = obj => arr => {
let result = arr.slice()
let idx = result.indexOf(obj)
if (idx !== -1) result.splice(idx, 1)
return result
}
console.dir(fn(obj)(data))
因為 splice()
需要提供 index
,直覺會使用 indexOf()
取得 index
,但因為傳入為 object,所以比較的是 reference。
因為 data
array 內的 object 與 obj
的 reference 不同,所以永遠找不到,因此 index
為 -1
,永遠無法如期刪除資料。
Array.prototype.findIndex()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
];
let obj = { title: 'RxJS in Action', price: 200 }
let fn = obj => arr => {
let result = arr.slice()
let idx = result.findIndex(x => x.title === obj.title && x.price === obj.price)
if (idx !== -1) result.splice(idx, 1)
return result
}
console.dir(fn(obj)(data))
indexOf()
有另外一個 findIndex()
版本,傳入的是 function。
由於我們要比較的是 object 內的 property value,而非 object reference,因此要使用的是 findIndex()
,傳入自己要判斷的 property value。
Array.prototype.filter()
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
];
let obj = { title: 'RxJS in Action', price: 200 }
let fn = obj => arr => arr.filter(x => !(
x.title === obj.title && x.price === obj.price
))
console.dir(fn(obj)(data))
splice()
的缺點是直接去修改 array 本身,所以之前都必須靠 slice()
先 clone 一份 array, 而且還必須先找到 index
,可改用 filter()
找到所有 不符合條件
的資料回傳。
Ramda
import { reject } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
];
let obj = { title: 'RxJS in Action', price: 200 }
let fn = obj => reject(x => x.title === obj.title && x.price === obj.price)
console.dir(fn(obj)(data))
filter()
需搭配反向邏輯,可改用 Ramda 的 reject()
搭配正向邏輯。
Function Composition
import { reject, compose, equals } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
];
let obj = { title: 'RxJS in Action', price: 200 }
let fn = compose(reject, equals)
console.dir(fn(obj)(data))
由於 Ramda 的 equals()
比較的不是 object 的 reference,事實上可直接將 equals()
與 reject()
組合即可。
Conclusion
indexOf()
找不到 object 是因為新建立 object 有新的 reference,因此indexOf()
找不到splice()
是直接修改 array;而filter()
是回傳新 array- 最簡單方式是將
equals()
與reject()
加以組合即可