들어가며
reduce() 함수를 사실 보면 코드가 읽히는데 사용할 순간이 쉽게 떠오르지 않아서 개념 및 원리를 정리하려 합니다.
reduce()
배열의 각 요소에 대해 주어진 리듀서 (reducer) 함수를 실행하고, 하나의 결과값을 반환한다. 즉, 쉽게 말하면 배열을 순회하며 각 요소에 대하여 이전의 콜백함수 실행 반환값을 전달하여 콜백함수를 실행하고 그 결과값을 반환한다.
reduce 메서드의 콜백 함수 4개의 인수
- 초기값 또는 콜백 함수의 이전 반환값
- reduce 메서드를 호출한 배열의 요소값
- reduce 메서드를 호출한 배열의 인덱스
- reduce 메서드를 호출한 배열 자체, 즉 this
const arr = [1, 2, 3, 4, 5];
/*
previousValue : 이전 콜백의 반환값
currentValue : 배열 요소의 값
currentIndex : 인덱스
array : 메소드를 호출한 배열, 즉 this
*/
// 합산
const sum = arr.reduce(function (previousValue, currentValue, currentIndex, self) {
console.log(previousValue + '+' + currentValue + '=' + (previousValue + currentValue));
return previousValue + currentValue; // 결과는 다음 콜백의 첫번째 인자로 전달된다.
})
console.log(sum); // 15: 1~5까지의 합
/*
1+2=3
3+3=6
6+4=10
10+5=15
15
*/
// 최대값 취득
const max = arr.reduce(function (pre, cur) {
return pre > cur ? pre : cur;
});
console.log(max); // 5: 최대값
- reduce 메서드는 초기값과 배열의 첫 번째 요소 값을 콜백 함수에게 인수로 전달하면서 호출하고 다음 순회에는 콜백 함수의 반환 값과 두 번째 요소값을 콜백 함수의 인수로 전달하면서 호출한다. 이런 과정을 반복하여 reduce 메서드는 하나의 결과값을 반환한다.
Array.prototype.reduce의 두 번째 인수로 초기값을 전달하기 → 이 값은 콜백 함수에 최초로 전달
const sum = [1, 2, 3, 4, 5].reduce(function (pre, cur) {
return pre + cur;
}, 5);
console.log(sum); // 20
// 5 + 1 => 6 + 2 => 8 + 3 => 11 + 4 => 15 + 5
객체의 프로퍼티 값을 합산하는 경우!
// 프로퍼티 값을 합산
const priceSum = products.reduce(function (pre, cur) {
console.log(pre.price, cur.price);
// 숫자값이 두번째 콜백 함수 호출의 인수로 전달된다. 이때 pre.price는 undefined이다.
return pre.price + cur.price;
});
console.log(priceSum); // NaN
이와 같은 결과를 나타내기에 객체의 프로퍼티 값을 합산 하는 경우에는 반드시 초기값을 전달해아 한다.
초기값 설정 후
const products = [
{ id: 1, price: 100 },
{ id: 2, price: 200 },
{ id: 3, price: 300 }
];
// 프로퍼티 값을 합산
const priceSum = products.reduce(function (pre, cur) {
return pre + cur.price;
}, 0);
console.log(priceSum); // 600
reduce 빈 배열 호출 시 에러 발생
const sum = [].reduce(function (pre, cur) {
consonle.log(pre, cur);
return pre + cur;
});
// TypeError: Reduce of empty array with no initial value
에러를 회피하기 위해선 초기값을 전달하면 된다.
그리고 초기값(initialValue)이 없을 땐 pre는 첫 번째 요소부터, cur는 두 번째 요소부터 전달되기 시작한다.
따라서 reduce를 호출할 때는 언제나 초기값을 전달하는 것이 안전하다.
reduce() 활용하기 → 예제
- 평균 구하기
const values = [1, 2, 3, 4, 5, 6];
const average = values.reduce(function (pre, cur, index, { length }) {
// 마지막 순회가 아니면 누적값을 반환하고 마지막 순회면 누적값으로 평균을 구해 반환한다.
return index === length - 1 ? (pre + cur) / length : pre + cur;
});// average()
console.log(average); // 3.5
- 중복 값 구하기
const count = fruits.reduce((acc, cur) => {
// 첫 번쨰 순회 시 acc는 초기값인 {}이고 cur은 첫 번쨰 요소인 'banana'다.
// 초기값으로 전달받은 빈 객체에 요소값인 cur을 프로퍼티 키로, 요소의 개수를 프로퍼티 값으로 할당한다.
// 만약 프로퍼티 값이 undefined(처음 등장하는 요소)이면 프로퍼티 값을 1로 초기화한다.
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {})
console.log(count);
// { banana: 1, apple: 2, orange: 2 }
결론
reduce()를 사용하면서 배열을 처리하는 로직을 명확하게 나타내어 가독성을 높일 수 있지만, 고차 함수를 남용한다면 성능의 문제가 발생할 수 있고, 익명 함수로 인해 가독성을 해칠 수 있다는 단점이 있습니다. 그리고 reduce()는 초기값을 잘못 설정하는 등의 실수가 발생할 수 있기 때문에 신중히 원리를 이해하고 올바르게 사용할 수 있도록 노력해야 합니다.
참고
- 도서 | 모던 자바스크립트 Deep Dive
- https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
- https://poiemaweb.com/js-array-higher-order-function
'Javascript' 카테고리의 다른 글
[Javascript] Map 객체 (0) | 2024.04.03 |
---|---|
[Web, React] 마우스 휠 가로 스크롤 작동 구현하기 (0) | 2024.03.29 |
[Javascript] 문자열 양쪽 끝 공백 제거하는 trim() (0) | 2024.03.20 |
[비동기 HTTP 통신] Ajax, Axios, Fetch에 대해 (1) | 2024.03.13 |