4

I'm trying to remove elements from an array based on an array of indices to remove elements from the array.

Here's my code:

const array = [0, 1, 2, 3, 4, 5];
const indicesToRemove = [0, 2, 5];

for (let i = indicesToRemove.length; i--;) {
    array.splice(indicesToRemove[i], 1);
}

console.log(array);

Result: [1, 3, 4]

It seems to be working fine but I wonder if there's a better/faster way to do the same thing.

Zack Lee
  • 2,192
  • 2
  • 18
  • 36
  • Does this answer your question? [How can I remove a specific item from an array?](https://stackoverflow.com/questions/5767325/how-can-i-remove-a-specific-item-from-an-array) – William Prigol Lopes May 03 '20 at 00:35
  • @WilliamPrigolLopes My question is more about performance rather than how to do it. – Zack Lee May 03 '20 at 00:56

3 Answers3

3

If you can put the array with those elements removed into a new variable name, I'd use filter to filter out indicies which are in indiciesToRemove:

const array = [0, 1, 2, 3, 4, 5];
const indicesToRemove = [0, 2, 5];

const filtered = array.filter((_, i) => !indicesToRemove.includes(i));

console.log(filtered);

For less computational complexity, use a Set instead, that way you only have to iterate over the indiciesToRemove once, rather than on every iteration:

const array = [0, 1, 2, 3, 4, 5];
const indicesToRemove = new Set([0, 2, 5]);

const filtered = array.filter((_, i) => !indicesToRemove.has(i));

console.log(filtered);
CertainPerformance
  • 260,466
  • 31
  • 181
  • 209
  • it's not faster – Chris May 03 '20 at 00:35
  • 1
    If the two arrays are large enough such that performance starts being an issue, the Set solution sure will be faster - it reduces the overall complexity from `O(n ^ 2)` to `O(n)` – CertainPerformance May 03 '20 at 00:36
  • Is your solution really faster than mine? – Zack Lee May 03 '20 at 00:55
  • It seems like mine is a bit faster than yours when I run the benchmark: https://jsben.ch/EyhFO – Zack Lee May 03 '20 at 01:00
  • Did you guys actually test it? It's pretty dependent on the size and contents of the array, and the positions of the elements you are removing. Varies pretty wildly. Splice ends up being orders of magnitude faster on large sets. – user120242 May 03 '20 at 01:06
  • @ZackLee If you have tiny datasets, performance doesn't matter at all, because the operation will be virtually instantaneous anyway. If you have large datasets, both the `.filter` and the `Set` method will be much faster than `splice`. – CertainPerformance May 03 '20 at 01:18
  • @CertainPerformance It seems like mine is even faster with larger datasets like 1,000: https://jsben.ch/qUwwj – Zack Lee May 03 '20 at 01:23
  • @Zack Lee Your benchmark code is wrong. One is doing a search every time, and the other is just removing positions without searching. – user120242 May 03 '20 at 01:27
  • @ZackLee You also aren't using the Set method - the Set method is what decreases the computational complexity from `O(n ^ 2)` to `O(n)`. This will have a *huge* positive effect in large datasets. – CertainPerformance May 03 '20 at 01:28
  • One is deleting indexes directly. The filter does a search every time. – user120242 May 03 '20 at 01:34
  • @CertainPerformance I added test with Set and it's still slower – Chris May 03 '20 at 01:36
1

How about this:

arr = array.filter( function(indicesToRemove) {
  return !toRemove.includes(indicesToRemove);
} );

console.log(arr)
Jackson
  • 1,183
  • 1
  • 2
  • 14
1

Splice is the fastest method

const array = [0, 1, 2, 3, 4, 5];
const indicesToRemove = [0, 2, 5];

for (let index = indicesToRemove.length; index >= 0 ; index--) {
  array.splice(index, 1);
}

and if you work with a lot of data and make new arrays, such as filter or push, you will have memory problems.

Benchmarking:

// setup
const array1 = [...new Array(5000).keys()];
const indicesToRemove1 = [...new Array(5000).keys()];

const array2 = [...new Array(5000).keys()];
const indicesToRemove2 = [...new Array(5000).keys()];

const array3 = [...new Array(5000).keys()];
const indicesToRemove3 = [...new Array(5000).keys()];

const array4 = [...new Array(5000).keys()];
const indicesToRemove4 = new Set([...new Array(5000).keys()]);

test 1

for (let index = indicesToRemove1.length; index >= 0 ; index--) {
    array1.splice(index, 1);
}
// 3,398 ops/s ±0.71%
// fastest

test 2

for (let index = indicesToRemove2.length; index--;) {
    array2.splice(index, 1);
}
// 3,377 ops/s ±0.53%
// 0.62% slower

test 3

const newArray = array4.filter((_,i) => !indicesToRemove4.has(i))
// 3,322 ops/s ±0.5%
// 2.25% slower

test 4

const newArray = array3.filter((_,i) => !indicesToRemove3.includes(i))
// 22 ops/s ±5.6%
// 99.35% slower
Chris
  • 1,253
  • 8
  • 13
  • But isn't it dangerous to remove elements in a forward loop? Wouldn't it affect the original array index? – Zack Lee May 03 '20 at 01:08