그러던 도중 우연찮게 우테코 5기 프리코스를 진행했고 , 여기서 jest로 테스트코드를 작성하는데 fn()모킹을 메소드 체이닝으로 구현하는 것을 reduce로 매우 간편하게 사용하는 것을 보고 매우 놀랐던 기억이 있다
reduce함수는 단지 덧셈만 사용할 수 있는 것이 아니라 , 메소드체이닝을 비롯해 map , filter , sort 등등의 수많은 배열 메소드들을 구현할 수 있었다
그래서 실제로 reduce메소드를 바탕으로 (덧셈을 제외하고)할 수 있는 것들에 대해서 알아보았다
reduce의 기본 동작은 초깃값을 지정하고 에서 배열을 순회하며 누적값을 따로 생성하며
로직을 수행하는 것입니다
class School {
student = 10;
name = 'winterClass';
enroll(count) {
this.student += count;
return this;
}
printStatus() {
console.log(`student count is ${this.student}`);
}
}
const school = new School();
let numbers = [2, 7, 3]; // enroll할 학생 수
// school.enroll(2).enroll(7).enroll(3)
numbers.reduce((acc, value) => {
return acc.enroll(value);
}, school);
school.printStatus();
2명 , 7명 , 3명에 대해서 enroll()체이닝을 진행합니다
school.enroll(2).enroll(7).enroll(3)
return acc.enroll(value);
enroll()메소드는 return this를 해줘야 합니다 enroll()한 결과값을 누산기에 누적 하는 것이므로 반드시 enroll()은 school객체를 리턴해야 합니다acc.enroll(value);
return acc;
enroll()메소드는 return this를 안 해줘도 됩니다 enroll()메소드의 결과값을 누산기에 누적하는 게 아니라 현재 acc인 school객체를 따로 누산기에 누적하기 때문입니다reduce는 map의 기능또한 구현할 수 있습니다
// map
const oneTwoThree = [1, 2, 3];
let resultForMap = oneTwoThree.map((v) => {
if (v % 2) {
return '홀수';
}
return '짝수';
});
console.log(resultForMap); // [ '홀수', '짝수', '홀수' ]
초깃값을 배열로 만들고
, 배열에 값들을 push하면 map과 같아집니다.
// reduce
const oneTwoThree = [1, 2, 3];
let resultForReduce = oneTwoThree.reduce((acc, cur) => {
acc.push(cur % 2 ? '홀수' : '짝수');
return acc;
}, []);
console.log(resultForReduce); // [ '홀수', '짝수', '홀수' ]
이를 응용해서 조건부로 push를 하면 filter와 같아집니다.
아래의 코드는 1,2,3중에서 1보다 큰 값 중에서 홀짝을 판별하는 코드입니다
const oneTwoThree = [1, 2, 3];
let resultForReduce = oneTwoThree.reduce((acc, cur) => {
if (cur > 1) {
acc.push(cur % 2 ? '홀수' : '짝수');
}
return acc;
}, []);
console.log(resultForReduce); // [ '짝수', '홀수' ]
눈치 챘듯이 , reduce로 구현하는 map과 filter는 거의 차이가 없습니다
초깃값에서 배열을 순회하며 로직을 수행하는 것이기 때문에 연산을 걸어주든 , 조건을 걸어주든 모두 자유롭게 가능하기 때문입니다
로직을 수행했는데 동일한 배열 요소에 매핑이 된거면 map이 수행된거고
로직을 수행했는데 배열을 필터링 한거면 filter가 수행된 것입니다
그래서 sort, every, some, find, findIndex, includes도 다 reduce로 구현 가능하게 됩니다
💡 그렇지만 보다 직관적이고 , 보다 사용처에 맞는 빠른 연산을 위해 각각의 메소드를 사용하는 것이 권장됩니다reduce는 비동기 프로그래밍을 할 때에도 유용합니다
초깃값을 Promise.resolve()로 한 후에, return된 프로미스에 then을 붙여 다음
누적값으로 넘기면 , 프로미스가 순차적으로 실행됨을 보장할 수 있습니다.
const promiseFactory = (time) => {
return new Promise((resolve, reject) => {
console.log(time);
setTimeout(resolve, time);
});
};
[1000, 2000, 3000, 4000].reduce((acc, cur) => {
return acc.then(() => promiseFactory(cur));
}, Promise.resolve());
// 바로 1000
// 1초 후 2000
// 2초 후 3000
// 3초 후 4000
2차원 이상의 배열들을 flatten 할 때 배열을 순회하면서
concat (이어 붙이는) 로직을 reduce 를 활용해서 구현할 수 있습니다.
const data = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const flatArrayReducer = (accumulator, value, index, array) => {
return accumulator.concat(value);
};
const flattenedData = data.reduce(flatArrayReducer, []);
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
배열을 순회하면서 배열의 값으로 들어있는 object 의 key 존재여부를 확인하고,
unique 한 “cast 를 key 로 갖는 배열의 값들”을 최종적으로 return 하는 로직을 구현할 수 있습니다.
const input = [
{
title: '슈퍼맨',
year: '2005',
cast: ['장동건', '권상우', '이동욱', '차승원'],
},
{
title: '스타워즈',
year: '2013',
cast: ['차승원', '신해균', '장동건', '김수현'],
},
{
title: '고질라',
year: '1997',
cast: [],
},
];
const flatMapReducer = (accumulator, value, index, array) => {
const key = 'cast';
if (value.hasOwnProperty(key) && Array.isArray(value[key])) {
value[key].forEach((val) => {
if (accumulator.indexOf(val) === -1) {
accumulator.push(val);
}
});
}
return accumulator;
};
const flattenCastArray = input.reduce(flatMapReducer, []);
console.log(flattenCastArray)
// ['장동건', '권상우', '이동욱', '차승원', '신해균', '김수현']
let votes = ['kim', 'hong', 'lee', 'hong', 'lee', 'lee', 'hong'];
let reducer = function (accumulator, value, index, array) {
if (accumulator.hasOwnProperty(value)) {
accumulator[value] = accumulator[value] + 1;
} else {
accumulator[value] = 1;
}
return accumulator;
};
let result = votes.reduce(reducer, {});
console.log(result); // { kim: 1, hong: 3, lee: 3 }
(사실 아래 코드가 더 간단하긴 한듯)
let votes = ['kim', 'hong', 'lee', 'hong', 'lee', 'lee', 'hong'];
let result = {};
for (let i of votes) {
result[i] === undefined ? (result[i] = 1) : (result[i] += 1);
}
console.log(result);
출처 :