...
Array에는 map()이나 filter()나 join() 같은 메소드들이 있다. 사실 위의 기능은 모두 reduce로 구현이 가능하다. for in, for of, forEach 를 몰라도 생 for문으로 모두 구현할 수 있듯이 말이다.
한마디로 reduce는 Array메소드들의 아버지라고도 불리울수도 있다. 또한 initialValue에 배열이나 객체를 주면 전역으로 객체변수를 하나 만드는 듯한 효과를 낼 수 도 있다.
reduce 고차함수
- 배열의 왼쪽부터 콜백 함수를 실행 후 누산함
배열.reduce( function(acc, cur, index, arr) {
} [, initialValue] )
- 누산기accumulator (acc)
- 현재 값 (cur)
- 현재 인덱스 (idx)
- 원본 배열 (src)
- initialValue(optional : 사용해도 되고 안해도 되고) : callback의 최초 호출에서 첫 번째 인수에 제공하는 값.
초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용합니다. 빈 배열에서 초기값 없이 reduce()를 호출하면 오류가 발생합니다.
const numbers = [1, 2, 3, 4];
// 일반 for문
let result = 0;
for (i = 0; i < numbers.length; i++) {
result += numbers[i];
}
console.log(result); // 20;
//-----------------------------------------
const result = numbers.reduce((number1, number2) => number1 + number2);
/*
1,2 => 3 배열값 1번째, 2번째 부터 시작
3,3 => 6
6,4 => 10
*/
console.log(result); // 10;
//-------------------------------------------
const result = numbers.reduce((number1, number2) => number1 + number2, 10);
/*
10,1 => 11 initialValue값, 배열값 1번째 부터 시작
11,2 => 13
13,3 => 16
16,4 => 20
*/
console.log(result); // 20;
주어진 값들을 reduce를 통해 합산하는 방법이다.
물론 for문을 사용해서도 이와 같은 작업을 할 수는 있지만 권장하지는 않는다.
map, filter, reduce와 같은 함수는 기본적으로 순차적으로 값을 접근한다라는 개념을 가지기 때문에 for문을 사용할 때 작성해야 하는 반복문을 작성하지 않고도 사용할 수 있다.
이밖의 다른 산술 결과도 가능하다.
const numbers = [1, 2, 3, 4];
const sumByPlus = numbers.reduce((number1, number2) => number1 + number2);
const sumByMinus = numbers.reduce((number1, number2) => number1 - number2);
const sumByMulti = numbers.reduce((number1, number2) => number1 * number2);
console.log(sumByPlus); // 10
console.log(sumByMinus); // -8
console.log(sumByMulti); // 24
reduce의 initValue 속성
initValue값이 없는 경우
var res = [0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array){
console.log(`accumulator : ${accumulator}`);
console.log(`currentValue : ${currentValue}`);
console.log(`currentIndex : ${currentIndex}`);
console.log(" ");
return accumulator + currentValue;
});
console.log("res:", res); //10
initialValue를 제공하지 않으면, reduce()는 인덱스 1부터 시작해 콜백 함수를 실행하고 첫 번째 인덱스는 건너 뛴다.
reduce()함수 호출시 initialValue 값이 없는 경우
- accumulator 는 배열의 첫번째 값
- currentValue 는 배열의 두번째 값
콜백은 4번 호출된다. 각 호출의 인수와 반환값은 다음과 같다.
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 |
reduce()가 반환하는 값으로는 마지막 콜백 호출의 반환값(10)을 사용한다
initValue값이 있는 경우
var res = [0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array){
console.log(`accumulator : ${accumulator}`);
console.log(`currentValue : ${currentValue}`);
console.log(`currentIndex : ${currentIndex}`);
console.log(" ");
return accumulator + currentValue;
}, 10); // 누산기 초깃값 10을 주었다.
console.log("res:", res); //20
initialValue를 제공하면 인덱스 0에서 시작한다.
reduce()함수 호출시 initialValue 값이 있는 경우
- accumulator 는 initialValue
- currentValue 는 배열의 첫번째 값
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 |
reduce 활용 실전 예제
객체 배열에서의 값 합산
객체로 이루어진 배열에 들어 있는 값을 합산하기 위해서는 반드시 initialValue초기값을 주어 각 항목이 여러분의 함수를 거치도록 해야 한다.
var initialValue = 0;
var list = [
{ x : 1 },
{ x : 2 },
{ x : 3 }
];
var sum = list.reduce(function (accumulator, currentValue) {
return accumulator + currentValue.x;
}, initialValue)
console.log(sum) // logs 6
중첩 배열 펼치기
var arr = [
[0, 1],
[2, 3],
[4, 5]
]
var flattened = arr.reduce(function(accumulator, currentValue) {
return accumulator.concat(currentValue);
// return acc = [...acc, ...cur];
}
,[]);
console.log(flattened); //[0, 1, 2, 3, 4, 5]
속성으로 객체 분류하기
age가, 나이가 몇이냐 에 따라 객체를 재정렬 정리해서 묶어서 반환하는 코드.
var peoples = [
{
name : 'Alice',
age : 21
},
{
name : 'Max',
age : 20
},
{
name : 'Jane',
age : 20
}
];
function groupBy(objectArray, property){
return objectArray.reduce(function (accumulator, currentObj) {
var key = currentObj[property];
console.log(`key : ${key}`);
if (!accumulator[key]) {
accumulator[key] = [];
}
accumulator[key].push(currentObj);
return accumulator;
}, {}); // 누산기를 산술에 쓰지않고 빈 객체나 배열을 줘서 여기에다 담는 용도로도 쓴다.
};
var groupedPeople = groupBy(peoples, 'age');
console.log(`groupedPeople : ${JSON.stringify(groupedPeople)}`);
/*
"age" 속성으로 객체 분류 (나이 별)
groupedPeople : {
"20":[{"name":"Max","age":20},{"name":"Jane","age":20}],
"21":[{"name":"Alice","age":21}]
}
*/
배열의 중복 항목 제거
사실 이렇게 하는것보다 중복이 없다는 set 자료형을 이용해 그냥 new Set(arr)하면 끝이긴하다. reduce를 이런식으로 활용할수 있다는 개념정도는 알자
let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
let result = arr.sort().reduce((accumulator, currentValue) => {
const length = accumulator.length
if (length === 0 || accumulator[length - 1] !== currentValue) {
accumulator.push(currentValue);
}
return accumulator;
}, []);
console.log(result);
//[1,2,3,4,5]
filter 를 reduce 로 구현
[ 2, 4, 5, 6, 8 ].filter(val => val % 2 === 0) // [2, 4, 6, 8]
[ 2, 4, 5, 6, 8 ].reduce((acc, val) => {
if(val % 2 === 0)
acc.push(val)
return acc;
}, []); // [2, 4, 6, 8]
filter + map 조합을 reduce 하나로 구현
[ 2, 4, 5, 6, 8 ]
.filter(val => val % 2 === 0)
.map(val => val * 10); // [20, 40, 60, 80]
// 루프 한번에 filter, map 완료.
[ 2, 4, 5, 6, 8 ].reduce((acc, val) => {
if(val%2 === 0) {
acc.push(val * 10)
}
return acc;
}, []);
2차원 배열 풀기
[ [9,2], [8,7] ].reduce((acc, val) => [ ...acc, ...val ], []) // [9, 2, 8, 7]
프로미스를 순차적으로 실행하기
function runPromiseInSequence(arr, input){
return arr.reduce(function(accumulator, currentFunction){
console.log(`accumulator : ${accumulator}`);
console.log(`currentFunction : ${currentFunction}`);
console.log(` `);
return accumulator.then(currentFunction);
// accumulator가 resolve해서 input값을 줘서 then에서 input을 인자라 받아서 함수 실행하는 격
}, Promise.resolve(input));
};
function promise1(value){
return new Promise(function(resolve, reject){
console.log(`promise1 -value : ${value}`)
resolve(value * 5);
});
};
function promise2(value){
return new Promise(function(resolve, reject){
console.log(`promise2 -value : ${value}`)
resolve(value * 2);
});
};
function function3(value){
return value * 3;
};
function promise4(value){
return new Promise(function(resolve, reject){
console.log(`promise4 -value : ${value}`)
resolve(value * 4);
});
};
const promiseArr = [promise1, promise2, function3, promise4, ]
runPromiseInSequence(promiseArr, 10)
.then(function(value){
console.log(`result value : ${value}`);
});
reduceRight 고차함수
- reduce 는 왼쪽 원소부터 콜백 함수를 실행
- reduceRight 는 오른쪽 원소부터 콜백 함수를 실행
var arr = ["경기도", "안양시", "만안구"];
var result = arr.reduceRight((acc, element) => acc + " " + element);
console.log(result); // 만안구 안양시 경기도
console.log(typeof result); // string
위 코드는 마치 Array.join(' ') 같지 않은가?
이처럼 reduce는 모든걸 구현할수 있는 강력한 만능 도구이다.
# 참고자료
https://www.zerocho.com/category/JavaScript/post/5acafb05f24445001b8d796d
https://steemit.com/javascript/@rouka/reduce
https://bbaktaeho-95.tistory.com/35
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.