우리는 흔히 배열을 반복해야 할 경우 forEach
, for
, for..of
를 사용한다. 혹은 각 요소를 돌면서 반복 작업을 수행하고, 이를 새로운 배열 형태로 얻기 위해서 map
을 사용한다. 지금 소개하고자 하는 reduce
와 reduceRight
메서드 역시 이와 비슷하지만, 조금 더 정교하다. 이 두 메서드는 배열에 기반을 둔 값 하나를 도출하기 위해 사용된다.
let value = arr.reduce(function(accumulator, item, index, array) {
// ...
}, [initial]);
인수로 넘겨주는 함수는 배열의 모든 요소를 대상으로 차례로 적용되는데, 적용 결과는 다음 함수를 호출할 때 적용된다.
reduce
의 인수는 다음과 같다.
메서드가 실행되면, 이전의 함수 호출 결과는 다음 함수의 첫 번째 인자로 넘겨진다. 즉, accumulator
는 이전까지의 함수 실행 결괏값을 누적하고 있는 것이다. 그리고 반복이 종료됐을 때 그 값이 reduce
의 결과가 된다. 복잡하다면 예시를 통해 살펴보자.
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
console.log(result); // 15
위의 코드는 reduce
메서드를 이용해서 1부터 5까지 담겨있는 배열의 모든 요소들의 합을 구하는 코드이다. 모든 인수가 사용되지는 않았고, accumulator, item, initial만 사용되었다. 그림으로 본다면 조금 더 이해가 쉬울 것이다.
1. 함수가 처음 실행되었을 때 sum
은 인자로 주어진 initial
값인 0이 되고, current
는 배열의 첫 번째 요소의 값인 1이 된다. sum
은 0 + 1이 되어 반환된다.
2. 함수가 두 번째 실행되었을 때, sum
은 1이 되고, current
는 2가 된다. 그리고 sum
은 1 + 2가 되어 반환된다.
이러한 과정을 반복하며 배열의 마지막 요소까지 접근한다.
만약 배열 내 요소의 반복을 거꾸로 하고 싶다면, reduceRight
를 사용하면 된다.
initial
값을 생략할 수도 있는데, 이렇게 되면 reduce
는 배열의 첫 번째 요소를 초기값으로 취하고 반복을 두 번째 요소부터 시작한다. 첫 번째 반복에서 sum
은 1이 되고, current
는 2가 된다.
하지만 주의해야 할 점이 있다. 만약 배열이 비어 있다면, initial
값을 인수로 갖지 않는 reduce
는 오류를 일으킨다.
let arr = [];
// TypeError: Reduce of empty array with no initial value
arr.reduce((sum, current) => sum + current);
다음은 정수 배열 numbers
를 인수로 받아 해당 배열의 평균값을 출력하는 함수이다. 이를 reduce
를 사용해서 리팩토링하면 다음과 같이 표현할 수 있다.
// reduce를 사용하지 않고 표현한 함수
let average = (numbers) => {
let sumOfArray = 0;
for (let num of numbers) {
sumOfArray += num;
}
return sumOfArray / numbers.length;
};
// reduce를 사용하여 표현한 함수
let average = (numbers) => {
return numbers.reduce((sum, current) => sum + current) / numbers.length;
};