Remove a single element from an array
pop method to remove the last element
If you want to remove the last element, pop
method can be used for all data types.
{
const array = [5, 4, 3, 2, 1, 0, 9];
console.log(array.pop()) // 9
console.log(array) // [ 5, 4, 3, 2, 1, 0 ]
}
{
const array = ["aa", "bb", "cc"];
console.log(array.pop()) // cc
console.log(array) // ["aa", "bb"]
}
shift method to remove the first element
In opposite, shift
method removes the first element.
{
const array = [5, 4, 3, 2, 1, 0, 9];
console.log(array.shift()) // 5
console.log(array) // [ 4, 3, 2, 1, 0, 9 ]
}
{
const array = ["aa", "bb", "cc"];
console.log(array.shift()) // aa
console.log(array) // [ 'bb', 'cc' ]
}
Combination of indexOf and splice
If you want to remove an element in the middle of the array, you need to first know the index of the target element. indexOf
method returns the index if it exists in the array.
Then, remove it with splice
method. The second argument is always 1 in this case. The second argument is the number of elements that will be deleted.
{
const array = [5, 2, 1, 0, 2];
const index = array.indexOf(2);
console.log(index) // 1
array.splice(index, 1);
console.log(array) // [ 5, 1, 0, 2 ]
}
{
const array = ["aa", "bb", "cc"];
const index = array.indexOf("bb");
console.log(index); // 1
array.splice(index, 1);
console.log(array) // [ 'aa', 'cc' ]
}
But as you can see, it deletes only the first occurrence. Note that this is an exact match. If you write array.indexOf("b")
to search a string that contains “b”, it doesn’t work as expected.
Remove multiple elements from an array
indexOf loop to remove multi occurrences
If you want to delete multiple elements, it needs to be done in a loop.
{
const array = [5, 2, 1, 0, 2];
for (let i = 0; ; i++) {
const index = array.indexOf(2);
console.log(`${i}: ${index}`)
// 0: 1
// 1: 3
// 2: -1
if (index < 0) {
break;
}
array.splice(index, 1);
}
console.log(array)
// [ 5, 1, 0 ]
}
{
const array = ["aa", "bb", "cc", "bb"];
for (let i = 0; ; i++) {
const index = array.indexOf("bb");
console.log(`${i}: ${index}`)
// 0: 1
// 1: 2
// 2: -1
if (index < 0) {
break;
}
array.splice(index, 1);
}
console.log(array)
// [ 'aa', 'cc' ]
}
The second statement, where the loop condition is normally defined, in for is empty because the length of the array changes when deleting an element from the array.
Instead, it checks whether the array still has the target value or not. indexOf
method returns -1 if the target value is not found in the array. If the target value is not found anymore, it breaks the loop.
Compare the value one by one in a for loop
I think the previous way is not so good if the array is very big. I guess for-loop is used in indexOf
to find the index. If there are multiple elements to delete, it calls it multiple times. It might cause a performance issue.
Let’s implement it in for-loop here.
{
const array = [2, 2, 1, 0, 2];
for (let i = array.length - 1; i >= 0; i--) {
if (array[i] === 2) {
console.log(i)
// 4
// 1
// 0
array.splice(i, 1);
}
}
console.log(array)
// [ 1, 0 ]
}
{
const array = ["aa", "bb", "cc", "bb"];
for (let i = array.length - 1; i >= 0; i--) {
if (array[i] === "bb") {
console.log(i)
// 3
// 1
array.splice(i, 1);
}
}
console.log(array)
// [ 'aa', 'cc' ]
}
It starts from the last element because the length of the array changes when deleting an element from the array. If it starts from the end, it doesn’t affect the result.
This is an NG implementation.
// NOT WORK as expected
const array = [2, 2, 1, 0, 2];
for (let i = 0; i < array.length; i++) {
if (array[i] === 2) {
console.log(i)
// 0
// 3
array.splice(i, 1);
}
}
console.log(array)
// [ 2, 1, 0 ]
How to remove an element from an object array
indexOf for exact match
indexOf
is not so useful for an object because it is an exact match.
const obj = { prop1: 4, prop2: "d" };
const array = [
{ prop1: 1, prop2: "a" },
{ prop1: 2, prop2: "b" },
{ prop1: 3, prop2: "c" },
];
array.push(obj);
const notFound = array.indexOf({ prop1: 1, prop2: "a" });
console.log(notFound); // -1
const index = array.indexOf(obj);
console.log(index); // 3
array.splice(index, 1);
console.log(array);
// [
// { prop1: 1, prop2: 'a' },
// { prop1: 2, prop2: 'b' },
// { prop1: 3, prop2: 'c' }
// ]
For the first indexOf
call, it doesn’t find the element even though the values are the same as the first element. The value is the same but the object itself is different.
If you want to delete the object, you need to pass precisely the same object as you can see on the second indexOf
call.
findIndex for loose match
Use findIndex
instead if you can’t get precisely the same object.
const array = [
{ prop1: 1, prop2: "a" },
{ prop1: 2, prop2: "b" },
{ prop1: 3, prop2: "c" },
{ prop1: 4, prop2: "d" }
];
const index = array.findIndex((value) => value.prop1 === 3 && value.prop2 === "c");
console.log(index); // 2
array.splice(index, 1);
console.log(array);
// [
// { prop1: 1, prop2: 'a' },
// { prop1: 2, prop2: 'b' },
// { prop1: 4, prop2: 'd' }
// ]
You can define which properties should be compared to find the element.
How to remove multiple elements from an object array
If you need to remove multiple elements from an object array, you need to remove them in a for-loop. Of course, findIndex
can be used here but let’s not use it here.
It’s basically the same as number/string version.
const array = [
{ prop1: 1, prop2: "a" },
{ prop1: 2, prop2: "b" },
{ prop1: 3, prop2: "c" },
{ prop1: 4, prop2: "d" },
{ prop1: 2, prop2: "bb" },
];
for (let i = array.length - 1; i >= 0; i--) {
if (array[i].prop1 === 2) {
console.log(i)
// 4
// 1
array.splice(i, 1);
}
}
console.log(array);
// [
// { prop1: 1, prop2: 'a' },
// { prop1: 3, prop2: 'c' },
// { prop1: 4, prop2: 'd' }
// ]
Add remove methods to Array interface (prototype)
If you often remove an element in many places, it’s better to define the function in the Array interface in the following way so that you can simply call the methods like array.remove()
.
declare global {
interface Array<T> {
removeByPerfectMatch(value: T): void;
removeByLooseMatch(predicate: (value: T, index: number, obj: T[]) => unknown): void;
removeAllByPerfectMatch(value: T): void;
removeAllByLooseMatch(predicate: (value: T, index: number, obj: T[]) => unknown): void;
}
}
Array.prototype.removeByPerfectMatch = function (value) {
const index = this.indexOf(value);
if (index >= 0) {
this.splice(index, 1);
}
}
Array.prototype.removeByLooseMatch = function (predicate) {
const index = this.findIndex(predicate);
if (index >= 0) {
this.splice(index, 1);
}
}
Array.prototype.removeAllByPerfectMatch = function (value) {
for (let i = this.length - 1; i >= 0; i--) {
if (this[i] === value) {
this.splice(i, 1);
}
}
}
Array.prototype.removeAllByLooseMatch = function (predicate) {
for (let i = this.length - 1; i >= 0; i--) {
if (predicate(this[i], i, this)) {
this.splice(i, 1);
}
}
}
The usage is the same as the other methods. You can call those methods after a dot.
const obj = { prop1: 444, prop2: "ddd" };
const array = [
{ prop1: 111, prop2: "aaa" },
{ prop1: 222, prop2: "bbb" },
{ prop1: 333, prop2: "ccc" },
{ prop1: 222, prop2: "bbb" },
{ prop1: 444, prop2: "ddd" },
{ prop1: 222, prop2: "bbb" },
];
array.push(obj);
array.push(obj);
array.push(obj);
let before = array.length;
array.removeByPerfectMatch(obj);
console.log(`length: ${before} -> ${array.length} (removeByPerfectMatch)`);
console.log(array);
// length: 9 -> 8 (removeByPerfectMatch)
// [
// { prop1: 111, prop2: 'aaa' },
// { prop1: 222, prop2: 'bbb' },
// { prop1: 333, prop2: 'ccc' },
// { prop1: 222, prop2: 'bbb' },
// { prop1: 444, prop2: 'ddd' },
// { prop1: 222, prop2: 'bbb' },
// { prop1: 444, prop2: 'ddd' },
// { prop1: 444, prop2: 'ddd' }
// ]
There is the same value element on index 4 but it is not removed because it is a different object.
before = array.length;
array.removeByLooseMatch((value) => value.prop1 === 222)
console.log(`length: ${before} -> ${array.length} (removeByLooseMatch)`);
console.log(array);
// length: 8 -> 7 (removeByLooseMatch)
// [
// { prop1: 111, prop2: 'aaa' },
// { prop1: 333, prop2: 'ccc' },
// { prop1: 222, prop2: 'bbb' },
// { prop1: 444, prop2: 'ddd' },
// { prop1: 222, prop2: 'bbb' },
// { prop1: 444, prop2: 'ddd' },
// { prop1: 444, prop2: 'ddd' }
// ]
This method removes the first occurrence.
before = array.length;
array.removeAllByLooseMatch((value) => value.prop1 === 222);
console.log(`length: ${before} -> ${array.length} (removeAllByLooseMatch)`);
console.log(array);
// length: 7 -> 5 (removeAllByLooseMatch)
// [
// { prop1: 111, prop2: 'aaa' },
// { prop1: 333, prop2: 'ccc' },
// { prop1: 444, prop2: 'ddd' },
// { prop1: 444, prop2: 'ddd' },
// { prop1: 444, prop2: 'ddd' }
// ]
Then, remove all the occurrences. Two elements are removed.
before = array.length;
array.removeAllByPerfectMatch({ prop1: 444, prop2: "ddd" });
console.log(`length: ${before} -> ${array.length} (removeAllByPerfectMatch)`);
console.log(array);
// length: 5 -> 5 (removeAllByPerfectMatch)
// [
// { prop1: 111, prop2: 'aaa' },
// { prop1: 333, prop2: 'ccc' },
// { prop1: 444, prop2: 'ddd' },
// { prop1: 444, prop2: 'ddd' },
// { prop1: 444, prop2: 'ddd' }
// ]
This call doesn’t remove anything because the passed object is newly created on the call. The same object doesn’t exist in the array.
before = array.length;
array.removeAllByPerfectMatch(obj);
console.log(`length: ${before} -> ${array.length} (removeAllByPerfectMatch)`);
console.log(array);
// length: 5 -> 3 (removeAllByPerfectMatch)
// [
// { prop1: 111, prop2: 'aaa' },
// { prop1: 333, prop2: 'ccc' },
// { prop1: 444, prop2: 'ddd' }
// ]
This method call removes two elements. { prop1: 444, prop2: 'ddd' }
still exists in the array because it was added in the initialization. However, the other two elements were added by push(obj)
method. Therefore, only the two elements were removed here.
Simple Performance comparison indexOf loop vs for loop
function indexOfLoop(array: number[], deleteValue: number) {
for (let i = 0; ; i++) {
const index = array.indexOf(deleteValue);
if (index < 0) {
break;
}
array.splice(index, 1);
}
}
function simpleLoop(array: number[], deleteValue: number) {
for (let i = array.length - 1; i >= 0; i--) {
if (array[i] === deleteValue) {
array.splice(i, 1);
}
}
}
const count = 1000000;
const array = Array.from(Array(count).keys()).map(x => Math.floor(Math.random() * 100));
const array2 = [...array];
console.time("indexOfLoop");
indexOfLoop(array, 36);
console.timeEnd("indexOfLoop");
console.time("simpleLoop");
simpleLoop(array2, 36);
console.timeEnd("simpleLoop");
I use Node.js 14.13.0. The build command was tsc --lib "es2019,dom"
.
indexOfLoop | simpleLoop |
20.576s | 4.080s |
19.245s | 3.494s |
29.464s | 3.703s |
23.681s | 5.672s |
When I built it with this tscondig, the difference becomes smaller for some reason… Why?
indexOfLoop | simpleLoop |
12.226s | 9.573s |
12.985s | 10.583s |
17.819s | 13.955s |
14.653s | 12.042s |
Check this post if you want to remove duplicates.
Comments