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()加以組合即可