1

I've been working with JavaScript arrays recently, and I've come across a situation where I need to remove certain items from an array. Here's an example of what I mean:

let myArray = ['apple', 'banana', 'cherry', 'date', 'elderberry'];

Let's say I need to remove the 'banana' and 'date' from this array. What would be the most efficient way to achieve this? I would ideally like the solution to be scalable, in case I need to remove more items in the future.

I know that I could use a loop to iterate through the array and splice the array when the item is found, like so:

for (let i = 0; i < myArray.length; i++) {
    if (myArray[i] === 'banana' || myArray[i] === 'date') {
        myArray.splice(i, 1);
        i--;
    }
}

However, I'm wondering if there's a better, more JavaScript-esque way to accomplish this task. Also, I've heard that using splice within a loop can lead to issues due to the changing indices.

Any assistance you could provide would be greatly appreciated.

I am expecting better results.

  • 6
    PSA: [`filter()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) exists. [Examples](https://stackoverflow.com/questions/1187518/how-to-get-the-difference-between-two-arrays-in-javascript). – tadman Jul 31 '23 at 14:56
  • Check here [link](https://stackoverflow.com/questions/7310559/the-best-way-to-remove-array-element-by-value) – Sudipto Bhattacharya Jul 31 '23 at 15:06
  • `within a loop can lead to issues due to the changing indices`, yes that's why you did -> `i--;` after you spliced, another option to avoid that is do the loop in reverse. – Keith Jul 31 '23 at 15:40

2 Answers2

0

I would not worry about performance and just go for Array.prototype.filter() as:

  • It's the simplest and easier to read/understand implementation.
  • Works with multiple occurrences of the same item.
  • Seems to be the fastest anyway (this might change depending on the shape and size of you data and browser).

Anyway, here's a playground with different option for you to take a look and experiment:

function measure(name, callback, runs = 100000) {
  const t0 = performance.now();
  
  for (let i = 0; i <= runs; ++i) {
    callback()
  }
  
  const t1 = performance.now() - t0;
  
  console.log(name, t1, 'ms')
}

const myArray = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
const myElementsToRemove = ['banana', 'date'];

function removeMultipleWithSpliceClone(arr, elementsToRemove) {
  const newArr = arr.slice(0)
  
  for (const elementToRemove of elementsToRemove) {
    // This won't work for multiple ocurrences of the same item:
    const index = newArr.indexOf(elementToRemove)
    
    if (index >= 0) {
      newArr.splice(index, 1);
    }
  }
  
  return newArr;
}

function removeMultipleWithSpliceMutate(arr, elementsToRemove) { 
  for (const elementToRemove of elementsToRemove) {
    // This won't work for multiple ocurrences of the same item:
    const index = arr.indexOf(elementToRemove)
    
    if (index >= 0) {
      arr.splice(index, 1);
    }
  }
  
  return arr;
}

function removeMultipleWithFilter(arr, elementsToRemove) {
  return arr.filter((element) => !elementsToRemove.includes(element))
}

function removeMultipleWithForAndPush(arr, elementsToRemove) {
  const newArr = []
  
  for (const element of arr) {
    if (!elementsToRemove.includes(element)) newArr.push(element)
  }
  
  return newArr;
}

// console.log(removeMultipleWithSpliceClone(myArray, myElementsToRemove))
// console.log(removeMultipleWithSpliceMutate(myArray, myElementsToRemove))
// console.log(removeMultipleWithFilter(myArray, myElementsToRemove))
// console.log(removeMultipleWithForAndPush(myArray, myElementsToRemove))

measure('removeMultipleWithSpliceClone', () => {
  removeMultipleWithSpliceClone(myArray, myElementsToRemove)
})

measure('removeMultipleWithSpliceMutate', () => {
  removeMultipleWithSpliceMutate([...myArray], myElementsToRemove)
})

measure('removeMultipleWithFilter', () => {
  removeMultipleWithFilter(myArray, myElementsToRemove)
})

measure('removeMultipleWithForAndPush', () => {
  removeMultipleWithForAndPush(myArray, myElementsToRemove)
})
Danziger
  • 19,628
  • 4
  • 53
  • 83
  • `I would not worry about performance`, Totally agree with that statement. But a slight observation, -> `removeMultipleWithSplice` , if your going to use `splice` your likely wanting to mutate the array, your version copies the array and then mutates taking away any speed advantage, also using `forEach` would have a performance impact here too, a-> `for (let x = arr.length -1; x >= 0; x --) {` would likely give you a performance boost, as it avoids callback overhead. ps: the descending loop is deliberate as it avoids index issues the OP was talking about. – Keith Aug 01 '23 at 10:10
  • @Keith You are right, I've updated the answer to include an option that mutates the array you pass and changed `forEach` to `for`. I'm not using the descending loop thought as my examples do not iterate the elements in the array you want to filter like the one provided by the OP. – Danziger Aug 02 '23 at 16:58
0

Use the Array indexOf() function ti find the index of an element, and then Array.splice() to remove it, in a loop for all elements to be removed.

Optional checks depending on circumstances: Is there such an element? Is there another such an element?

toreric
  • 332
  • 4
  • 9