Array.prototype.remove(item)
Isiah Meadows
isiahmeadows at gmail.com
Fri Nov 10 10:38:44 UTC 2017
I find myself frequently using this code to remove an item from an array:
```js
var index = array.indexOf(item)
if (index >= 0) array.splice(index, 1)
```
That's nice, but there's a couple problems:
1. It only removes the first element, not all occurrences in the array
(which is usually what you want).
2. I know of no engine that fuses those calls, and it'd be rather
difficult to do so.
My proposal is this: add a new `Array.prototype.remove(item)` method
that basically does this:
- Accept a list of items to remove, passed as arguments.
- Remove all instances of those items from the array.
- Return `true` if any entries were removed, `false` otherwise.
And in the process, it should take care to do it in an optimized
fashion, avoiding the overhead of allocation, excess reads/writes, and
excess branching.
In case you're curious why I'm not using `Set`, here's why that's
ill-equipped in many cases:
- Sets take up a *lot* more memory than arrays, by necessity.
- Sets are stored typically in tree-like structures, making for poor
iteration performance.
- If you're frequently iterating, but infrequently adding/removing,
these two will quickly kill your performance.
It rarely comes up in day-to-day coding, but the difference is
substantial when you're writing lower-level data structures and
performance-sensitive code. It's probably down a similar vein to
`Array.prototype.copyWithin`, which was pretty much designed for
`memcpy` or `memmove`.
Here would be a polyfill of what I'd like to propose (it uses a
minimum of writes, by design, and it does everything in one pass):
```js
if (typeof Array.prototype.remove !== "function") {
// Private utility
const includes = (items, itemLength, entry) => {
for (let j = 0; j < itemLength; j++) {
if (items[j] === entry) return true
}
return false
}
Array.prototype.remove = function (item) {
const oldLength = this.length
const newLength = arguments.length
// This is technically observably no different than
const items = [...arguments]
let newLength = 0
for (let i = 0; i < oldLength; i++) {
const entry = this[i]
if (includes(items, newLength, entry)) {
let newLength = i++
while (i !== list.length) {
const entry = this[i]
if (!includes(items, newLength, entry)) {
this[newLength++] = entry
}
i++
}
this.length = newLength
for (let i = newLength; i < oldLength; i++) delete this[i]
return true
}
}
return false
}
}
```
If the variadic version wasn't accepted, there's also the non-variadic version:
```js
if (typeof Array.prototype.remove !== "function") {
Array.prototype.remove = function (item) {
const oldLength = this.length
let newLength = 0
for (let i = 0; i < oldLength; i++) {
const entry = this[i]
if (entry === item) {
let newLength = i++
while (i !== list.length) {
const entry = this[i]
if (entry !== item) this[newLength++] = entry
i++
}
this.length = newLength
for (let i = newLength; i < oldLength; i++) delete this[i]
return true
}
}
return false
}
}
```
-----
Isiah Meadows
me at isiahmeadows.com
Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com
More information about the es-discuss
mailing list