reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.
리듀서 함수는 누산기 accumulator(acc), 현재 값 (cur), 현재 인덱스 (idx), 원본 배열 (src)의 네 개의 인자를 가집니다. 함수의 반환 값은 누산기에 할당되고, 누산기는 순회 중 유지되므로 결국 최종 결과는 하나의 값이 됩니다.
배열의 각 요소에 대해 실행할 callback 함수는 다음 네 가지 인수를 받습니다. 이 중 currentIndex와 array 인수는 선택 사항입니다.
1. accumulator
누산기 accmulator는 콜백의 반환값을 누적합니다. 콜백의 이전 반환값 또는, 콜백의 첫 번째 호출이면서 initialValue를 제공한 경우에는 initialValue의 값입니다.
2. currentValue
처리할 현재 요소입니다.
3. currentIndex
처리할 현재 요소의 인덱스입니다. initialValue를 제공한 경우 0, 아니면 1부터 시작합니다. 필수 요소는 아닙니다.
4. array
reduce()를 호출한 배열입니다.
그리고 initialValue 가 있습니다. initialValue는 callback의 최초 호출에서 첫 번째 인수에 제공하는 값입니다. 이 초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용합니다. 빈 배열에서 초기값 없이 reduce()를 호출하면 오류가 발생합니다.
초기값이 제공되는 여부에 따라서 콜백함수의 최초 호출 때 accumulator와 currentValue는 다음 두 가지 값 중 하나를 가질 수 있습니다.
1) initialValue를 제공한 경우
accumulator는 initialValue와 같고 currentValue는 배열의 첫 번째 값과 같습니다.
2) initialValue를 제공하지 않은 경우
accumulator는 배열의 첫 번째 값과 같고 currentValue는 배열의 두 번째 값과같습니다.
다음의 예제처럼 initialValue을 제공하지 않으면 출력 가능한 형식이 세 가지이므로, 보통 초기값을 주는 것이 더 안전합니다.
var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
var maxCallback2 = ( max, cur ) => Math.max( max, cur );
// initialValue 없이 reduce()
[ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42
[ { x: 22 } ].reduce( maxCallback ); // { x: 22 }
[ ].reduce( maxCallback ); // TypeError
// map/reduce로 개선 - 비었거나 더 큰 배열에서도 동작함
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );
인자를 네 개나 가지고 있고 직관적으로 한 눈에 이해하기는 어려운 함수입니다. 이러한 이유로 reduce 함수를 원소들의 합을 구하는 함수로 주로 이해하기도 합니다.
[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
return accumulator + currentValue;
});
콜백 함수는 4번 호출됩니다. 각 호출의 인수와 반환값은 다음과 같습니다.
callback | accumulator | currentValue | currentIndex | array | 반환 값 |
---|---|---|---|---|---|
1번째 호출 | 0 | 1 | 1 | [0, 1, 2, 3, 4] | 1 |
2번째 호출 | 1 | 2 | 2 | [0, 1, 2, 3, 4] | 3 |
3번째 호출 | 3 | 3 | 3 | [0, 1, 2, 3, 4] | 6 |
4번째 호출 | 6 | 4 | 4 | [0, 1, 2, 3, 4] | 10 |
reduce()가 반환하는 값으로는 마지막 콜백 호출의 반환값(10)을 사용하는 것을 확인할 수 있습니다. 이처럼 reduce는 배열의 값을 더하는 데 유용한 함수입니다. 그러나 reudce로 할 수 있는 것은 더욱 많습니다.
const Number = [1, 2, 3];
result = Number.reduce((acc, cur) => {
acc.push(cur % 2 ? '홀수' : '짝수');
return acc;
}, []);
result; // ['홀수', '짝수', '홀수']
reduce의 초기값에 빈 배열을 넣어 주고 빈 배열에 값들을 push해주었습니다. map으로도 같은 결과를 도출해낼 수 있습니다.
const Number = [1, 2, 3];
let result = Number.map((v) => {
return (v % 2) ? '홀수' : '짝수'
result; // ['홀수', '짝수', '홀수']
})
});
reduce는 이처럼 map처럼 활용할 수 있습니다. map()이 배열 각 요소에 대해서 주어진 함수를 수행한 결과를 모아 새로운 배열을 반환하는 메서드라는 것을 생각할 때 더욱 쉬운 이해가 가능합니다. reduce 초기값으로 배열을 넣어주었기 때문에 map처럼 배열을 반환할 수 있었습니다.
참고로, map 함수는 세 개의 매개 변수를 갖는데 value, index, array입니다. reduce 함수가 accumulator 매개 변수를 갖는 것만 비교하면 같은 것을 볼 수 있습니다.
map뿐만 아니라 filter, sort, every, some, find, findIndex, includes 등의 함수도 reduce 함수로 구현할 수 있습니다.
+) Filter 함수
Reduce 함수를 얘기할 때, map 함수만큼이나 자주 비교되는 것이 Filter 함수입니다. Filter 함수는 배열 각 요소에 대하여 주어진 함수의 결과값이 true인 요소들만을 모아서 새로운 배열을 반환하는 함수입니다. map의 콜백 함수의 리턴 값이 string, number, array 등이 다양했던 것에 비해 filter의 콜백 함수의 리턴 값은 오직 boolean입니다.
const Number = [1, 2, 3]
let arr = Number.filter((v) => (v % 2 === 0))
arr; // [2]
filter 함수를 reduce 함수로 나타내 보았습니다. 마찬가지로 초기값에 빈 배열을 주어서 배열을 반환할 수 있게 하였습니다.
const Number = [1, 2, 3]
let arr = Number.reduce((acc, cur) => {
if (cur % 2 === 0) {
acc.push(cur);
}
return acc;
}, []);
arr; // [2]