reduce()?
reduce는 자바스크립트의 Array 객체에 내장되어 있는 매서드로,
개인적으로 가장 좋아하는 매서드중의 하나이다.
작동 방식은 기본적으로 2가지의 인자를 가지는데,
배열을 순환하면서 계속해서 들고갈 acc, 그리고 현재 원소인 cur을 갖는다.
쉽게 말하면, map()이나 filter()의 경우엔 각각의 원소들을 순회하면서 이전 원소나, 다음 원소에 대한 정보를 알기가 편하지가 않은데, reduce의 경우 acc에 다양한 형태로 배열을 순환하면서 생기는 결과값들을 저장 할 수가 있다.
리턴하는 결과값도 새로운 배열이 아닌 acc를 리턴하기 때문에 결과는 배열일수도, 객체일수도, 숫자일수도, 문자일수도있다.
가장 간단한 예로, [1,2,3,4,5] 라는 배열의 모든 원소의 합을 구할때,
acc에 계속해서 더해주는 식으로 사용할 수 가 있다.
음... 쉽게 말하면 보따리를 하나 가지고 각각의 원소를 한번씩 돌아가며 방문해서 넣을지 말지 결정하거나,
원소를 바꿔서 보따리에 넣거나 한뒤 마지막에 보따리를 준다고 생각하면 쉬울듯?..
기본 사용예시
const numberArr = [1,2,3,4,5];
const totalSum = numberArr.reduce((acc, cur) => {
acc += cur;
return acc;
}, 0)
console.log(totalSum);
//15
이해를 돕기 위해 한국어로 변수명을 바꿔보면 아래와 같다.
const numberArr = [1,2,3,4,5];
const totalSum = numberArr.reduce((누적되고 있는 값, 현재값) => {
누적되고 있는 값 += 현재값; //누적되고 있는 값에 현재값을 더해준다
return 누적되고 있는 값; // 누적되고 있는 값을 reduce 내의 함수에서 리턴해준다.
}, 0) // 누적되고 있는 값의 초기값을 정해줄 수 있다.
console.log(totalSum);
//15
여기서 시작값을 10으로 하고, 10에다가 1부터 5까지의 합을 구하고 싶다면 0 부분을 10으로 바꿔주면 된다.
const numberArr = [1,2,3,4,5];
const totalSum = numberArr.reduce((누적되고 있는 값, 현재값) => {
누적되고 있는 값 += 현재값; //누적되고 있는 값에 현재값을 더해준다
return 누적되고 있는 값; // 누적되고 있는 값을 reduce 내의 함수에서 리턴해준다.
}, 10) // 누적되고 있는 값의 초기값이 10이 된다.
console.log(totalSum);
//25
// 계산은 10 + 1 + 2 + 3 + 4 + 5가된다.
시작값을 다양하게 설정해서 정말 다양한 경우의 문제에 대응할 수가 있는데, 예시를 알아보자.
map() 혹은 filter()의 대체
기본적으로 map() 매서드는 filter 처럼 특정 원소를 제외를 하지못하고, 무조건 원본 배열과 같은 길이의 배열을 리턴해준다. 그리고, filter() 매서드는 map 처럼 각각의 원소를 바꿔줄 수가 없다.
const numberArr = [1,2,3,4,5];
const addOneArr = numberArr.map(el => el + 1);
console.log(addOneArr)
// [2,3,4,5,6]
// - - -
const numberArr = [1,2,3,4,5];
const underThreeArr = numberArr.filter(el => el < 3);
console.log(underThreeArr)
// [1, 2]
이때 각각의 원소에 1을 더한 후, 그 값이 3보다 작은 원소들의 배열을 구해야 한다면 아래와 같이 할 수 있다.
const numberArr = [1,2,3,4,5];
const addOneUnderThreeArr = numberArr.map(el => el + 1).filter(el => el < 3);;
console.log(addOneUnderThreeArr)
// [2]
매서드 체이닝을 사용한다 해도 뭔가.. 음.. 꽤나.. 음.. 가독성이 떨어지는 느낌이다.
아래와 같이 reduce로 map()과 filter의 기능을 완벽하게 대체 할 수 있다.
const numberArr = [1,2,3,4,5];
const addOneArr = numberArr.map(el => el + 1);
const addOneArrReduce = numberArr.reduce((acc, cur) => {
acc.push(cur + 1);
return acc;
}, []);
console.log(addOneArr)
console.log(addOneArrReduce)
// [2,3,4,5,6]
// [2,3,4,5,6]
// - - -
const numberArr = [1,2,3,4,5];
const underThreeArr = numberArr.filter(el => el < 3);
const underThreeArrReduce = numberArr.reduce((acc,cur) => {
if(cur < 3) acc.push(cur);
return acc;
}, []);
console.log(underThreeArr)
console.log(underThreeArrReduce)
// [1, 2]
// [1, 2]
그렇다면 위의 map() 이후 filter()를 하던 부분을 reduce로 하나의 매서드로 변경 하면 아래와 같다.
const numberArr = [1,2,3,4,5];
const addOneUnderThreeArr = numberArr.map(el => el + 1).filter(el => el < 3);;
const addOneUnderThreeArrReduce = numberArr.reduce((acc, cur) => {
if(cur + 1 < 3) acc.push(cur + 1);
return acc;
}, []);
console.log(addOneUnderThreeArr)
console.log(addOneUnderThreeArrReduce)
// [2]
// [2]
변수명이 길어서 그렇지 두개의 매서드를 체이닝 하는것보다는 깔끔하게 쓸 수 있다.
객체를 이용해 각 원소의 갯수 구하기
제일 많이 사용하는 사례인데, acc를 객체로 지정 후, 새로운 키를 만들어서 값을 넣어주는 방식이다.
const randomArr = ["a", "a", "b", "c", "d", "a", "b", "d", "e", "f"];
// 위와 같은 배열이 있을때 여기서 각각 a,b,c,d,e,f 의 갯수를 담는 객체를 만들어보자면,
const numberOfElObj = randomArr.reduce((acc, cur) => {
if(acc[cur]) acc[cur] += 1; // acc객체에 cur이라는 키가 있다면 +1
else acc[cur] = 1; // acc객체에 cur이라는 키가 없다면 1로 초기화
return acc;
}, {})
console.log(numberOfElObj)
//{
"a": 3,
"b": 2,
"c": 1,
"d": 2,
"e": 1,
"f": 1
}
console.log(numberOfElObj.a)
// 3
console.log(numberOfElObj.b)
// 2
...
이런식으로 간편하게 해당 배열의 모든 원소가 각각 몇개인지를 알아낼 수 있다.
조금 더 응용하면 객체 내부에 새로운 객체를 만들어서, 각각의 원소가 몇개인지, 각각 몇번째 원소인지를 배열로 저장해 줄 수도 있다.
const randomArr = ["a", "a", "b", "c", "d", "a", "b", "d", "e", "f"];
// 위와 같은 배열이 있을때 여기서 각각 a,b,c,d,e,f 의 갯수를 담는 객체를 만들어보자면,
const numberOfElObj = randomArr.reduce((acc, cur, idx) => {
if(acc[cur]){
acc[cur].qty += 1;
acc[cur].idx.push(idx);
}else{
acc[cur] = {"qty" : 1, idx: [idx]};
}
return acc;
}, {})
console.log(numberOfElObj)
/*
{
"a": {
"qty": 3,
"idx": [0,1,5]
},
"b": {
"qty": 2,
"idx": [2,6]
},
"c": {
"qty": 1,
"idx": [3]
},
"d": {
"qty": 2,
"idx": [4,7]
},
"e": {
"qty": 1,
"idx": [8]
},
"f": {
"qty": 1,
"idx": [9]
}
}
*/
익숙해지면 다양한 활용법이 있어 다량의 복잡한 데이터를 다룰 경우에도 문제 없이 처리가 가능해진다.