How to remove a specific item from an array in JavaScript

There are two answers, and which one you want depends on a question nobody asks out loud: do you want a new array, or do you want this array changed?

filter or splice

const a = ["a", "b", "c", "b"];
a.filter(x => x !== "b");        // new array
const i = a.indexOf("b");
if (i > -1) a.splice(i, 1);      // this array
start           : [ 'a', 'b', 'c', 'b' ]
filter !== 'b'  : [ 'a', 'c' ]  <- removes ALL 'b'
indexOf+splice  : [ 'a', 'c', 'b' ]  <- removes the FIRST 'b' only

Same input, different results, and neither is wrong. filter tests every element, so every match goes. indexOf finds one position, so one element goes.

The other difference:

after filter, original : [ 'a', 'b', 'c', 'b' ]
after splice, original : [ 'a', 'c', 'b' ]  <- changed

splice mutates. That matters the moment anyone else holds a reference to the same array.

The guard is not optional

Leave off the if and the miss case does something specific and bad:

indexOf('zzz') : -1
splice(-1, 1)  : [ 'a', 'b', 'c' ]  <- removed the LAST item instead of nothing

A negative index counts from the end, so -1 means “the last one”. Searching for something that is not there deletes a real element — no error, no warning, and the element it removed is one you never named. Always check > -1.

delete is not it

delete d[1];
after delete d[1] : [ 'a', <1 empty item>, 'c', 'b' ]
length            : 4  <- unchanged
1 in d            : false

delete removes the value, not the slot. You are left with a hole, an unchanged length, and an array that behaves oddly in anything that iterates it.

splice tells you what it took

splice(1, 2) returned: [ 'b', 'c' ]  leaving [ 'a', 'b' ]

Second argument is a count, not an end index, and the return value is the removed elements — useful when you want to move something rather than drop it.

By index without mutating

t.toSpliced(1, 1) : [ 'a', 'c', 'b' ]  original still [ 'a', 'b', 'c', 'b' ]
typeof Array.prototype.toSpliced : function

toSpliced is splice that returns a copy. ES2023, so it is in current runtimes but not in whatever you are asked to support.

Objects compare by identity

filter(o => o !== {id:2}) : 2 items <- nothing removed
filter(o => o.id !== 2)   : [ { id: 1 } ]

Two objects with the same contents are not ===. Removing an object either means comparing a field, or holding the original reference.

Removing while looping forwards skips elements

forwards  : [ 'a', 'b', 'c' ]  <- one 'b' survived
backwards : [ 'a', 'c' ]

Splicing at index i shifts everything down one, so the next element slides into the slot the loop has just left. Iterate backwards, or use filter.

And for the ends there is no need for any of this:

pop()   : b  leaving [ 'a', 'b', 'c' ]
shift() : a  leaving [ 'b', 'c' ]

About Netcup (advertisement)

The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).