어떤 분야든 마찬가지겠지만 공부를 할 때, 배워서 어디에 써먹을 수 있는가?를 먼저 생각해보아야 한다.
문법만 배우고 실무에서 활용하는 방법을 모른다면 보람이 없다.
reduce()
는 배열의 4칙연산 뿐만 아니라 배열 요소의깊은 필터링
을 통해 원하는 정보들의 구성으로 이루어진배열을 반환
할 수 있다.
map()
이나filter()
보다 좀 더 다양한 활용이 가능하다.
reduce는 막상 들으면 뭘 감소시킨다는 거지? 라고 생각이 드는데, 사전을 검색해 보니 흔히 쓰이는 뜻이 아닌
변형 시키다
또는수정하다
라는 뜻도 있었다.배열을 가지고 다양한 방법으로 편집 가능하게 해주는 reduce 메서드에게 잘 어울리는 이름이었다.
reduce()
의 1번째 인자인 콜백함수는 배열 요소 수만큼 반복
하여 실행된다.reduce()
의 2번째 인자는 콜백함수의 첫번째 인자 초기값
인데(권장) 넣지 않으면 배열의 1번째 요소부터 연산한다.reduce()
의 인자
const arr = [1, 2, 3, 4, 5];
// 배열 모든 요소 합계
const sum = arr.reduce((acc, cur, i) => {
console.log('A :', acc, 'C :', cur, 'i :', i);
return acc + cur;
});
// A : 1 C : 2 i : 1
// A : 3 C : 3 i : 2
// A : 6 C : 4 i : 3
// A : 10 C : 5 i : 4
sum; // 15
// 초기값 0을 넣어도 결과는 동일하다.
const arr = [1, 2, 3, 4, 5];
// 배열 모든 요소 합계
const sum = arr.reduce((acc, cur, i) => {
console.log('A :', acc, 'C :', cur, 'i :', i);
return acc + cur;
}, 0);
// A : 0 C : 1 i : 0
// A : 1 C : 2 i : 1
// A : 3 C : 3 i : 2
// A : 6 C : 4 i : 3
// A : 10 C : 5 i : 4
sum; // 15
// 중국을 제외한 모든 나라들의 pop 의 합계
const arr = [
{ country: 'China',pop: 9 },
{ country: 'India', pop: 6 },
{ country: 'USA', pop: 10 },
{ country: 'Indonesia', pop: 5 }
]
const reducer = (acc, cur) => {
console.log('A :', acc, 'C.pop :', cur.pop);
return cur.country === 'China' ? acc : acc + cur.pop;
}
// A : 0 C.pop : 9
// A : 0 C.pop : 6
// A : 6 C.pop : 10
// A : 16 C.pop : 5
arr.reduce(reducer, 0); // 21
콜백 함수의 로직은 아래와 동일하다.
const reducer = (acc, cur) => {
console.log('A :', acc, 'C.pop :', cur.pop);
if (cur.country === 'China') {
return acc;
} else {
return acc + cur.pop;
}
}
const arr = [10, 30, 20];
const average = arr.reduce( (total, cur, index, arr) => {
console.log('T :', total, 'C :', cur, 'I :', index);
total += cur;
if ( index === arr.length - 1) { // 마지막 인덱스라면
return total / arr.length; // 평균 계산
} else {
return total; // 누적값
}
}, 0);
// T : 0 C : 10 I : 0
// T : 10 C : 30 I : 1
// T : 40 C : 20 I : 2
average; // 20
const arr = [4, 10, 30];
const doubled = arr.reduce( (total, cur) => {
console.log('T :', total, 'C :', cur);
total.push(cur * 2);
return total;
}, []);
// T : [] C : 4
// T : [8] C : 10
// T : [8, 20] C : 30
doubled; // [8, 20, 60]
사실 이런 케이스는
map()
을 쓰면 더 간단해진다.
const mapped = arr.map( el => el * 2 );
mapped; // [8, 20, 60]
// 5이상인 요소만 2배로 필터링 할 수도 있다.
const mapped = arr.map( el => el > 5 ? el * 2 : el );
mapped; // [4, 20, 60]
const arr = [4, 10, 30];
const result = arr.reduce( (total, cur) => {
console.log('T :', total, 'C :', cur);
if (cur > 5) {
total.push(cur * 2);
}
return total;
}, []);
// T : [] C : 4
// T : [] C : 10
// T : [20] C : 30
result; // [20, 60]
이 경우는
filter()
를 사용하고map()
을 한번 더 사용해줘야 같은 결과값을 얻을 수 있다.
간단한 연산의 경우 아래와 같이 두번 체이닝해서 사용하는 것이 코드가 짧아보이지만, 긴 연산이 들어간다면reduce()
로 한번에 처리하는 것이 더 깔끔하다.
const result = arr.filter( el => el > 5).map( el => el * 2)
result; // [20, 60]
const arr = ['banana', 'cherry', 'orange', 'apple', 'cherry', 'orange', 'apple', 'banana', 'cherry', 'orange', 'fig' ];
const fruit = arr.reduce( (obj, fruitName) => {
console.log('O :', obj, 'F :', fruitName);
obj[fruitName] = obj[fruitName] + 1 || 1;
return obj;
}, {})
// O : {} F : banana
// O : {banana: 1} F : cherry
// O : {banana: 1, cherry: 1} F : orange
// O : {banana: 1, cherry: 1, orange: 1} F : apple
// O : {banana: 1, cherry: 1, orange: 1, apple: 1} F : cherry
// O : {banana: 1, cherry: 2, orange: 1, apple: 1} F : orange
// O : {banana: 1, cherry: 2, orange: 2, apple: 1} F : apple
// O : {banana: 1, cherry: 2, orange: 2, apple: 2} F : banana
// O : {banana: 2, cherry: 2, orange: 2, apple: 2} F : cherry
// O : {banana: 2, cherry: 3, orange: 2, apple: 2} F : orange
// O : {banana: 2, cherry: 3, orange: 3, apple: 2} F : fig
fruit; // {banana: 2, cherry: 3, orange: 3, apple: 2, fig: 1}
아래 로직은 모두 같다.
obj[fruitName] = obj[fruitName] + 1 || 1;
obj[fruitName] = !obj[fruitName] ? 1 : obj[fruitName] + 1;
if (!obj[fruitName]) { // obj 객체에 해당 과일 프로퍼티가 없을 경우
obj[fruitName] = 1; // obj[과일이름] 에 1을 넣는다.
} else {
obj[fruitName] = obj[fruitName] + 1 // 값이 1이라도 있는 과일이라면, 기존 숫자에 1을 더해준다.
}
const arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const result = arr.reduce( (total, cur) => {
console.log('cur :', cur);
return total.concat(cur); // concat은 문자열 뿐만 아니라 배열도 이어 붙여서 반환한다.
// Array.prototype.concat() , String.prototype.concat()
}, []);
result; // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
const arr = [
{ a: 'happy', b: 'robin', c: ['blue','green'] },
{ a: 'tired', b: 'panther', c: ['green','black','orange','blue'] },
{ a: 'sad', b: 'goldfish', c: ['green','red'] }
];
const colors = arr.reduce((total, amount) => {
amount.c.forEach( color => { // c 배열의 각 요소 요소를
console.log('c :', color);
total.push(color); // total에 push 하여
})
return total;
}, [])
// c : blue
// 2 c : green
// c : black
// c : orange
// c : blue
// c : green
// c : red
// 누적된 하나의 배열로 반환
colors; // ['blue','green','green','black','orange','blue','green','red']
같은 색상이 중첩되지 않은 배열로 만들고 싶을 땐 아래와 같이
indexOf()
를 사용하여 필터링
const arr = [
{ a: 'happy', b: 'robin', c: ['blue','green'] },
{ a: 'tired', b: 'panther', c: ['green','black','orange','blue'] },
{ a: 'sad', b: 'goldfish', c: ['green','red'] }
];
const uniqueColors = arr.reduce((total, amount) => {
amount.c.forEach( color => {
if (total.indexOf(color) === -1){ // 배열에 해당 컬러가 없을 때만 push
console.log('c :', color);
total.push(color);
}
});
return total;
}, []);
// c : blue
// c : green
// c : black
// c : orange
// c : red
uniqueColors; // ['blue', 'red', 'green', 'black', 'orange']
// 이러한 pipeline 배열은 쉽게 수정 가능하다.
function increment(input) { return input + 1 }
function decrement(input) { return input - 1 }
function double(input) { return input * 2 }
function halve(input) { return input / 2 }
let pipeline = [increment, increment, double, decrement];
const result = pipeline.reduce( (total, func) => {
console.log('T :', total, ' F :', func);
return func(total);
}, 1);
// T : 1 F : increment(input) { return input + 1 }
// T : 2 F : increment(input) { return input + 1 }
// T : 3 F : double(input) { return input * 2 }
// T : 6 F : decrement(input) { return input - 1 }
result; // 5
초기값 1로
increment
함수를 두번 호출double
함수 한번,decrement
함수 한번 호출한 것과 같은 결과
reduce()
의 콜백 함수의 리턴 값은, 루프가 돌 때 첫번째 인자로 다시 넣어 사용할 값을 리턴하는 것이지, 결과를 리턴하는 것이 아니다.reduce()
가 작동하려면 콜백 함수 안에서 무언가는 반환시켜야 하고, 항상 내가 의도하고 실제로 원하는 값이 반환되는게 맞는지 반드시 체크해야 한다.
reduce()
는 사용하는 방법을 터득하기가 어렵지만 활용도가 높은 배열 메서드로 개념을 확실히 익혀 놓으면 데이터를 다룰 때 큰 도움이 되는 메서드로 보임.