배열은 다양한 메서드를 제공한다. 그 중
splice
,slice
,forEach
,filter
,map
,reduce
, 그리고reverse
에 대해 정리해본다.
arr.splice(start)
는 만능 스위스 맥가이버 칼 같은 메서드이다. 요소를 자유자재로 다룰 수 있게 해준다. 이 메서드를 사용하면 요소 추가, 삭제, 교체가 모두 가능하다.
문법은 다음과 같다.
arr.splice(index[, deleteCount, elem1, ...., elemN])
첫번째 매개변수는 조작을 가할 첫 번째 요소를 가리키는 인덱스(index)이다. 두번째 매개변수는 deleteCount로 제거하고자 하는 요소의 개수를 나타낸다. elem1, ..., elemN은 배열에 추가할 요소를 나타낸다.
splice
메서드를 사용해 작성된 예시 몇 가지를 보자.
먼저 요소 삭제에 관한 예시부터 살펴보면,
let arr = ['I', 'study', 'JavaScript'];
arr.splice(1, 1); // 인덱스 1부터 요소 한 개를 제거
alert( arr ); // ['I', 'JavaScript']
인덱스 1이 가리키는 요소부터 시작해 요소 한개를(1)를 지웠다.
다음 예시에선 요소 세개(3)를 지우고, 그 자리를 다른 요소 두개로 교체해 보도록 하자.
let arr = ['I', 'study', 'JavaScript', 'right', 'now']
// 처음(0) 세개(3)의 요소를 지우고, 이 자리를 다른 요소로 대체한다.
arr.splice(0, 3, 'Let's', 'dance']
alert( arr ) // now ['Let's', 'dance', 'right', 'now'];
splice
는 삭제된 요소로 구성된 배열을 반환한다. 아래 예시를 통해 확인해 보자.
let arr = ['I', 'study', 'JavaScript', 'right', 'now'];
//처음 두개의 요소를 삭제함.
let removed = arr.splice(0, 2);
alert( removed ); // 'I', 'study' <--삭제된 요소로 구성된 배열
splice
메서드의 deleteCount를 0으로 설정하면 요소를 제거하지 않으면서 새로운 요소를 추가할 수 있다.
let arr = ['I', 'study', 'JavaScript'];
// 인덱스 2부터
// 0개의 요소를 삭제한다.
// 그 후, 'complex'와 'language'를 추가한다.
arr.splice(2, 0, 'complex', 'language');
alert( arr ); // 'I', 'study', 'complex', 'language', 'JavaScript'
음수 인덱스도 사용할 수 있다.
slice메서드 뿐만 아니라 배열 관련 메서드엔 음수 인덱스를 사용할 수 있다. 이때 마이너스 부호 앞의 숫자는 배열 끝에서부터 센 요소 위치를 나타낸다.
let arr = [1, 2, 5];
// 인덱스 -1부터 (배열 끝에서부터 첫 번째 요소)
// 0개의 요소를 삭제하고
// 3과 4를 추가한다.
arr.splice(-1, 0, 3, 4);
alert( arr ); // 1, 2, 3, 4, 5
arr.slice
는 arr.splice
와 유사해 보이지만 훨씬 간단하다.
문법:
arr.slice([start], [end])
이 메서드는 'start'인덱스부터('end'를 제외한) 'end'인덱스까지의 요소를 복사한 새로운 배열을 반환한다. start와 end는 둘 다 음수일 수 있는데 이땐, 배열 끝에서부터의 요소 개수를 의미한다.
arr.slice
는 문자열 메서드인 str.slice
와 유사하게 동작하는데 arr.slice
는 서브 문자열 (substring
) 대신 서브 배열(subarray
)을 반환한다는 점이 다르다.
예시:
let arr = ['t', 'e', 's', 't'];
alert( arr.slice(1, 3) ); // e,s (인덱스가 1인 요소부터 인덱스가 3인 요소까지를 복사(인덱스가 3인 요소는 제외))
alert( arr.slice(-2) ); // s,t (인덱스가 -2인 요소부터 제일 끝 요소까지를 복사)
arr.slice
는 arr.slice()
처럼 인수를 하나도 넘기지 않으면서 호출할 수 있다. 이렇게 하면 arr
이 복사가 된다. 이런 방식은 기존의 배열을 건드리지 않으면서 배열을 조직해 새로운 배열을 만들고자 할 때 자주 사용된다.
arr.forEach
는 주어진 함수를 배열 요소 각각에 대해 실행할 수 있게 해준다.
문법:
arr.forEach(function(item, index, array) {
// 요소에 무언가를 할 수 있다.
});
아래는 요소 모두를 alert
창을 통해 출력해주는 코드이다.
// for each element call alert
['Bilbo', 'Gandalf', 'Nazgul'].forEach(alert);
아래는 인덱스 정보까지 더해서 출력해주는 좀 더 정교한 코드이다.
['Bilbo', 'Gandalf', 'Nazgul'].forEach((item, index, arry) => {
alert(`${item} is at index ${index} in ${array}`);
});
참고로, 인수로 넘겨준 함수의 반환값은 무시된다.
filter
는 find
와 문법이 유사하지만, 조건에 맞는 요소 전체를 담은 배열을 반환한다는 점에서 차이가 있다.
let results = arr.filter(function(item, index, array) {
// 조건을 충족하는 요소는 results에 순차적으로 더해진다.
// 조건을 충족하는 요소가 하나도 없으면 빈 배열이 반환된다.
});
예시:
let users = [
{id: 1, name: 'John'},
{id: 2, name: 'Pete'},
{id: 3, name: 'Mary'}
];
// 앞쪽 사용자 두명을 반환한다.
let someUsers = users.filter(item => item.id < 3);
alert(someUsers.length); // 2
arr.map
은 유용성과 사용 빈도가 아주 높은 메서드 중 하나이다.
map
은 배열 요소 전체를 대상으로 함수를 호출하고, 함수 호출 결과를 반환해준다.
문법:
let result = arr.map(function(item, index, array) {
// 요소 대신 새로운 값을 반환한다.
});
아래 예시에선 각 요소(문자열)의 길이를 출력해준다.
let lengths = ['Bilbo', 'Gandalf', 'Nazgul'].map(item => item.length);
alert(lengths); // 5,7,6
forEach
, for
, for..of
를 사용하면 배열 내 요소를 대상으로 반복 작업을 할 수 있다.
각 요소를 돌면서 반복 작업을 수행하고, 작업 결과물을 새로운 배열 형태로 얻으려면 map
을 사용하면 된다.
arr.reduce
와 arr.reduceRight
도 이런 메서드들과 유사한 작업을 해준다. 그런데 사용법이 조금 복잡하다. reduce
와 reduceRight
는 배열을 기반으로 값 하나를 도출할 때 사용된다.
문법:
let value = arr.reduce(function(accumulator, item, index, array) {
// ...
}, [initial]);
인수로 넘겨주는 함수는 배열의 모든 요소를 대상으로 차례차례 적용되는데, 적용 결과는 다음 함수 호출시 사용된다.
함수의 인수는 다음과 같다.
이전 함수 호출 결과는 다음 함수를 호출할 때 첫번째 인수(previousValue)로 사용된다.
첫 번째 인수는 앞서 호출했던 함수들의 결과가 누적되어 저장되는 '누산기(accumulator)'라고 생각하면 된다. 마지막 함수까지 호출되면 이 값은 reduce
의 반환값이 된다.
복잡해 보이긴 하지만 예제를 통해 메서드를 이해해 보자.
reduce
를 이용해 코드 한 줄로 배열의 모든 요소를 더한 값을 구해보자.
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 15
reduce
에 전달한 함수는 오직 인수 두개만 받고 있다. 대개 이렇게 인수를 두개만 받는다.
이제 어떤 과정을 거쳐 위와 같은 결과가 나왔는지 자세히 살펴보자.
함수 최초 호출시, reduce
의 마지막 인수인 0(초기 값)이 sum에 할당된다. current엔 배열의 첫번째 요소인 1이 할당된다. 따라서 함수의 결과는 1이 된다.
두번째 호출시, sum = 1
이고 여기에 배열의 두번째 요소( 2 )가 더해지므로 결과는 3이 된다.
세번째 호출시, sum = 3
이고 여기에 배열의 다음 요소가 더해진다. 이런 과정이 계속 이어진다.
한편, 아래와 같이 초기값을 생략하는 것도 가능하다.
let arr = [1, 2, 3, 4, 5];
// reduce에서 초기값을 제거함(0이 없음)
let result = arr.reduce(sum, current) => sum + current);
alert( result ); // 15
초기값을 없애도 결과는 동일하다. 초기값이 없으면 reduce
는 배열의 첫번째 요소를 초기값으로 사용하고 두번째 요소부터 함수를 호출하기 때문이다.
하지만 이렇게 초기값 없이 reduce
를 사용할 땐 극도의 주의를 기울어야 한다. 배열이 비어있는 상태면 reduce
호출 시 에러가 발생하기 때문이다.
예시:
let arr = [];
// TypeError: Reduce of empty array with no inital value
// 초기값을 설정해 주었다면 초기값이 반환되었을 것이다.
arr.reduce((sum, current) => sum + current);
이런 예외상황 때문에 항상 초기값을 명시해 줄 것을 권장한다.
arr.reduceRight
은 reduce
와 동일한 기능을 하지만 배열의 오른쪽부터 연산을 수행한다는 점이 다른 메서이다.
arr.reverse
는 arr
의 요소를 역순으로 정렬시켜주는 메서드이다.
예시:
let arr = [1, 2, 3, 4, 5];
arr.reverse();
alert( arr ); // 5,4,3,2,1
반환값은 재정렬된 배열이다.
지금까지 살펴본 배열 메서드를 요약해 본다.
splice(pos, deleteCount, ...items)
-pos부터 deleteCount개의 요소를 지우고 items 추가하기slice(start, end)
- start부터 end 바로 앞까지 의 요소를 복사해 새로운 배열을 만듦filter(func)
- func의 반환값을 true로 만드는 첫번째/전체 요소를 반환함.forEach(func)
- 모든 요소에 func을 호출함.map(func)
- 모든 요소에 func을 호출하고, 반환된 결과를 가지고 새로운 배열을 만듦reverse()
- 배열을 뒤집어 반환함.reduce(func, initial)
- 요소를 차례로 돌면서 func을 호출함. 반환값은 다음 함수 호출에 전달함.