[javascript] 자바스크립트 배열의 요소(들)를 삭제하기

초보자에게는 다소 어려운 내용입니다.

기초 내용을 먼저 보고 읽어보면 도움이 될수 있습니다.

> 배열 요소의 추가

> 배열 요소의 추가, 변경, 삭제하기

Splice() 메서드와 delete를 이용한 기초 삭제 방법

배열의 요소를 삭제하는 가장 기본적인 방법입니다.

배열의 위치 인덱스를 사용해 배열 아이템을 삭제합니다.

배열 아이템을 삭제하는 것은 동일하지만 "splice()"와 "delete"는 큰 차이점이 있습니다.

"splice()"는 배열 아이템을 삭제하면서 배열 길이가 줄어듭니다. 배열 아이템이 완전히 삭제됩니다.

"delete"는 배열 아이템을 삭제하지만, 배열이 차지하는 자리는 그대로 유지됩니다. 빈 배열 아이템이 유지되며, 배열 길이도 변하지 않습니다.

삭제 속도가 가장 빠른 방법이기 때문에 대량의 배열 아이템을 삭제할 때는 이 메서드를 사용하는 것이 좋습니다.

  1. splice(): 배열.splice(index, 1);
  2. delete: delete 배열[index];

인덱스로 배열 아이템 삭제

var array = [3,2,4,5,7,5,2];
var index = 3, number = 5;

//index 위치 배열 아이템 1개 삭제. 배열 길이가 줄어듬
array.splice(index, 1);
console.log(array);

//아이템 값이 같은 모든 배열 아이템 삭제
for (var i = 0; i < array.length; i++) {
    if (array[i] === number) { // 값이 같은 배열 인덱스 확인
        array.splice(i, 1);
    }
}
console.log(array);

//index 위치 배열 아이템 삭제. 배열 길이가 유지됨
delete array[index];
console.log(array);

//배열 아이템은 유지하고 값만 undefined 초기화
array[index-1] = undefined;
console.log(array);

//삭제한 배열 아이템의 값을 반환
var value = array.splice(index, 1)[0];
console.log(value);

실행 순서대로 콘솔에 배열을 표시하면 다음과 같이 표시됩니다. 3번째 줄의 "delete"로 삭제한 4번째 배열 아이템이 자리를 차지하고 있으면서 "empty"로 표시되는 것에 주의해야 합니다.

"undefined"는 자바스크립트에서 특수한 형태의 객체입니다. 배열이 초기화 되지초기화되지 않은 것이 아니라 "undefined" 값을 가지고 있는 배열 아이템입니다. 초기화되지 않은 배열 아이템은 empty로 표시됩니다.

값으로 배열 아이템(들) 삭제

아이템 값을 기준으로 배열 아이템을 삭제하는 함수는 다음과 같이 만들어 사용합니다.

단수형인 removeItem()은 배열의 처음 나오는 같은 값을 가진 배열 아이템을 삭제합니다. 첫번째 일치하는 값을 가진 배열 인덱스를 반환하는 메서드인 indexOf()를 사용합니다.

//아이템 값이 처음 나오는 배열 아이템 삭제
function removeItem(arr, value) {
  var index = arr.indexOf(value);
  if (index > -1) {
    arr.splice(index, 1);
  }
  return arr;
}
//아이템 값이 같은 모든 배열 아이템 삭제
function removeItems(arr, value) {
  var i = 0;
  while (i < arr.length) {
    if (arr[i] === value) {
      arr.splice(i, 1);
    } else {
      ++i;
    }
  }
  return arr;
}
console.log(removeItem([2,5,4,1,2,7,2], 2));
console.log(removeItems([2,5,4,1,2,7,2], 2));

두 번째 함수인 removeItems()는 while 루프를 돌면서 같은 값을 가진 배열 아이템들을 모두 삭제합니다.

Filter() 메서드를 이용한 아이템(들) 삭제

먼저 배열의 filter() 메서드가 하는 기능에 대해서 알아야 합니다.

배열에서 특정 아이템 값이 없는 배열을 얻는 방법을 알아보겠습니다.

var value = 2;
var arr = [1, 2, 3, 4, 5, 6, 2];

arr = arr.filter(function(item) {
    return item !== value;
});
console.log(arr);

filter() 메서드를 이용하면 루프문을 돌아서 같은 값을 가진 아이템 인덱스를 찾을 필요 없이 조건에 부합하는 아이템만을 반환하는 함수를 filter() 메서드의 매개변수로 정의해 새로운 배열을 반환받을 수 있습니다.

앞의 예에서 정의한 필터 함수는 "value" 값인 2가 아닌 아이템들만을 반환하는 함수를 파라미터로 받아 "[1, 3, 4, 5, 6]" 배열을 반환합니다.

"arr" 배열 변수가 아니라 새 변수에 반환되는 배열을 저장하면 아이템 값 2가 없는 새 배열을 만들 수 있습니다.

ES6 화살표 함수로 앞의 filter() 메서드를 개선하면 다음과 같이 단순화해서 작성할 수 있습니다.

var value = 2;
var arr = [1, 2, 3, 4, 5, 6, 2];

arr = arr.filter(item => item !== value);//화살표 함수로 필터 함수를 단순화
console.log(arr);

filter() 메서드로 여러개의 배열 아이템을 한꺼번에 삭제하려면 다음과 같이 includes() 메서드를 이용해 삭제할 값들을 담고 있는 배열의 값이 원 배열에 포함되어 있는지를 확인하면 됩니다.

let values = [2, 3, 4];
let arr = [1, 2, 3, 4, 5, 6, 7];

arr = arr.filter(item => !values.includes(item));
console.log(arr);

"values.includes(item)" 메서드는 "values" 배열에 item 값이 있는지를 비교합니다. 메서드 앞에 "!"를 붙여서 포함되지 않는 아이템만 골라내게 됩니다.

조금 더 고급스럽게 프로토타입 메서드로 만들어서 범용으로 사용할 수 있도록 만들면 다음과 같이 만들 수 있습니다.

Array.prototype.remove = function(...values){
    return this.filter(item => !values.includes(item));
}

let arr = [1, 2, 3, 4, 5, 6, 7];
console.log(arr.remove(2, 3, 5));

반환되는 배열은 "[1, 4, 6, 7]"이 됩니다. 앞서의 방법들보다 훨씬 직관적이고 재활용성이 높습니다.

다만 ES6로 작성된 코드여서 인터넷 익스플로러에서는 지원이 되지 않기 때문에 호환성 문제를 염두해야 할 경우 앞서의 구형 구현 방식을 사용해야 합니다.

slice() 메서드를 이용한 배열 범위 삭제

slice() 메서드를 이용하면 배열의 아이템 값을 대신해 시작 인덱스와 끝 인덱스 범위 안의 모든 배열 아이템을 삭제할 수 있습니다. 범위의 시작과 끝만 지정하면 되기 때문에 많은 배열 아이템을 삭제할 때 유용하게 사용할 수 있습니다.

시작과 끝 인덱스 사이의 모든 배열 아이템을 삭제하는 프로토타입 메서드는 다음과 같습니다.

slice() 메서드는 범위의 시작과 끝을 나타내는 2개의 파라미터를 사용합니다. 두 파라미터는 시작 인덱스 ~ 끝 인덱스-1 범위의 아이템을 가리키며, 이 범위 안의 배열 아이템(들)을 가진 새 배열을 반환합니다.

Array.prototype.remove = function(from, to) {
  let rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};

this.push.apply(this, rest)는 펼침 연산자를 사용한 this.push(...rest) 와 같습니다. 더 간결한 표현이므로 이 방법을 사용해도 됩니다.

구현 방식이 다소 생소할 수 있지만, 삭제할 범위 뒤쪽의 남는 배열은 "rest" 변수에 저장하고, 앞쪽의 남는 배열은 원 배열에 저장(this.length 로 배열 길이를 축소해 남김) 한 후 this.push.apply() 메서드로 "rest" 배열의 아이템들을 원 배열에 차례로 붙이는 것입니다.

뒤쪽의 남는 배열을 구할 때 "||" 비교 연산자로 여러 값을 비교를 하는 이유는 from, to 파라미터가 from 이 값이 더 작은 인덱스라는 보장이 없기 때문에 더 큰 값을 찾기 위해서입니다.

slice() 메서드를 이용하면 특정 위치의 아이템 한 개만을 삭제할 수도 있습니다. 다음 코드의 concat() 메서드는 2개의 배열을 합쳐서 하나의 배열로 만드는 메서드입니다.

삭제할 인덱스 앞쪽 배열과, 뒤쪽 배열을 slice()로 각각 만든 후 concat()로 하나로 합칩니다.

다만 concat() 메서드는 실행 속도가 (굉장히 아주) 많이 느리기 때문에 앞서 구현한 push.apply() 방식의 구현 방법을 사용하는 것을 더 권장합니다.

let items = [1, 3, 2, 5, 6, 8];
const i = 3;

items = items.slice(0, i).concat(items.slice(i+1, items.length));
console.log(items);

배열 양쪽 끝에 있는 아이템(들) 삭제 팁

첫 번째 배열 아이템, 또는 마지막 배열 아이템을 삭제할 때는 조금 더 간편한 방법을 사용할 수 있습니다.

배열의 아이템 갯수 정보를 가지고 있는 "length" 속성은 쓰기가 가능합니다. 

"arr.length = arr.length - 1"과 같은 수식을 사용할 수 있으며, 맨 마지막 배열 아이템을 삭제하는 것과 같은 효과를 가져옵니다. 줄어든 길이만큼 끝에서부터 개수만큼의 배열 아이템이 삭제됩니다.

첫 번째 아이템을 삭제할 때는 shift() 메서드를 사용합니다. shift() 메서드는 왼쪽 방향으로 배열 아이템을 하나씩 이동합니다. 맨 처음 배열 아이템은 0 인덱스에서 왼쪽으로 이동되면서 삭제됩니다.

const arr = [1, 2, 3, 4, 5, 6];
arr.length = 5; // Set length to remove element
console.log( arr ); // [1, 2, 3, 4, 5]


const arr = [1, 2, 3, 4];
arr.shift(); // returns 1
console.log( arr ); // [2, 3, 4]