본문 바로가기

JavaScript

배열의 reduce() 파헤치기

배열의 reduce() 함수에 대해 정리해보려고 한다.

내용은 MDN 웹 문서 사이트를 참고하여 정리된 내용이며, 더 많은 예제와 자세한 내용은 아래 링크에서 볼 수 있다.

링크로 가기 👉 Array.prototype.reduce()

 

reduce()

reduce() 메서드는 배열에 대하여 주어진 리듀서(reducer)함수를 실행하고 결과 값을 반환한다.

 

reduce()는 reduce(콜백함수, 초기값)와 같은 형태를 가지고 있으며, 배열의 각 요소가 주어진 콜백함수를 거치게 된다. 이 콜백함수를 리듀서(reducer) 라고 한다.

 

리듀서는 네가지 인자를 가진다.

  • accumulator(누산기): 누산기는 콜백(리듀서)의 반환 값을 누적한다. 만약 초기값이 제공된다면, 리듀서의 첫번째 호출 시 accumulator의 값은 초기값과 같다. 만약 초기값이 제공되지 않는다면 accumulator는 배열의 첫번째 값이 자동으로 들어가게된다.
  • currentValue: 현재 처리할 요소이다.
  • currentIndex(optional): 처리할 현재 요소의 인덱스이다. 초기값이 제공되지 않았다면 accumulator에 배열의 첫번째 값인 index 0의 값이 들어가고, currentIndex는 index 1부터 시작하게된다.
  • array(optional): reduce()를 호출한 배열

초기값은 최초의 리듀서 호출에서 accumulator(누산기)에 제공하는 값이다. 초기값이 없다면 배열의 첫번째 요소(0번 인덱스)를 사용하고 초기값이 있다면 주어진 초기값을 사용한다.

 

reduce()의 반환값은 각 요소가 리듀서를 거쳐 누적된 값의 결과 값이다.

 

초기값이 없다면 발생하는 경우

  1. 배열의 첫번째 요소(0번 인덱스)를 accumulator에 누적한 후 1번 인덱스부터 reducer를 거친다.
  2. 배열의 요소 값이 1개인데 초기값도 제공하지 않은 경우 or 초기값은 있지만 배열이 빈 배열인 경우에는 그 단독 값을 리듀서를 거치지 않고 바로 반환한다.
  3. 배열이 비었는데 초기값도 없는 상태에서 reduce()를 호출하면 TypeError 오류가 발생한다.

이렇게 초기값을 주지 않으면 발생할 수 있는 경우의 수가 3가지이므로 초기값은 주는 것이 안전하다.

const sumCallback = ( acc, cur ) => { return acc + cur };

// 초기값 없이 reduce()실행할 경우 3가지
[1, 2, 3].reduce( sumCallback ); // 1 + 2 + 3 = 6 : 초기값이 없으므로 0번 인덱스를 accumulator에 누적
[1].reduce( sumCallback ); // 1 : 배열의 요소 값이 1개이고 초기값 X
[].reduce( sumCallback, 1 ); // 1 : 빈 배열에 초기값 1
[ ].reduce( sumCallback ); // TypeError

 

초기값이 있는 경우와 없는 경우의 비교

 

초기값이 없는 경우
[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue;
}); //최종 반환 값 10

 

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

 

초기값이 없기 때문에, accumulator에 배열의 첫번째 값이 들어간 상태로 배열의 두번째 값 부터 리듀서를 거치기 시작한다. 따라서 콜백함수(리듀서) 호출은 4번만 실행된다.

 

 

초기값이 있는 경우 - 초기값 10
[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue;
}, 10); //최종 반환 값 20

 

callback accumulator currentValue currentIndex array 반환 값
1번째 호출 10 0 0 [0, 1, 2, 3, 4] 10
2번째 호출 10 1 1 [0, 1, 2, 3, 4] 11
3번째 호출 11 2 2 [0, 1, 2, 3, 4] 13
4번째 호출 13 3 3 [0, 1, 2, 3, 4] 16
5번째 호출 16 4 4 [0, 1, 2, 3, 4] 20

 

초기값이 10으로 주어졌다. 따라서 accumulator에 10이 들어간 상태에서 주어진 배열의 첫번째 값인 0부터 리듀서를 거치기 시작한다. 콜백함수(리듀서)는 5번 실행된다.

 

reduce() 사용해보기 

객체로 이루어진 배열에서의 값 합산

각 항목이 모두 리듀서를 거치게 하려면 반드시 초기값을 주어야 한다.

const initialValue = '';
const sum = [{x: 'Hello'}, {x: 'World'}, {x: '!'}].reduce(function (accumulator, currentValue) {
    return accumulator + (currentValue.x + '*'); //요소 뒤에 별표 추가
}, initialValue)

console.log(sum) //Hello*World*!*

 

배열내의 요소수를 세어서 객체로 반환하기

const fruits = ['Apple', 'Banana', 'Banana', 'Lemon', 'Apple', 'Kiwi'];

const countedFruits = fruits.reduce(function (allFruits, fruit) { 
  if (fruit in allFruits) { //누산된 객체 allFruits에 해당 fruit의 property가 있으면
    allFruits[fruit]++; //해당 fruit의 value값을 1 증가시킴
  }
  else {
    allFruits[fruit] = 1; //allFruits에 해당 fruit의 property값이 없으면 만들어주고 1을 넣어줌
  }
  return allFruits;
}, {});
// countedFruits
// { Apple: 2, Banana: 2, Lemon: 1, Kiwi: 1 }

 

배열의 중복 항목 제거하기

let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
let result = arr.sort().reduce((accumulator, current) => {
    const length = accumulator.length
    if (length === 0 || accumulator[length - 1] !== current) {
        accumulator.push(current);
    }
    return accumulator;
}, []);
console.log(result); //[1,2,3,4,5]