reduce 정의
배열의 빌트인 함수로서 시그니처는 다음과 같다.
// Array.prototype.reduce
arr.reduce(iterator [, initialValue]);
iterator
는 다음과 같은 인자를 받는다.
accumulator(
accumulated, // 콜백에서 반환한 누적 value
currentValue, // 현재 value
currentIndex, // 현재 index
context // reduce 를 호출한 현재 배열
)
활용 이모저모
reduce
는 보통 배열의 요소들을 하나의 결과로 합치는 용도로 많이 쓰게 된다.
실제 구글링할 경우 제일 많이 나오는 예제는 요소들의 합계를 구하는 용도로 많이 나온다.
const arr1 = [ 1, 2, 3, 4, 5 ];
arr1.reduce((a, b) => a + b); // 15
이런 간단한 용도 말고 좀더 활용을 한다면 타 배열의 함수 대부분을 대체할 수 있는 막강한 힘이 있다.
filter 를 reduce 로 구현
const isEven = val => val%2 === 0;
[ 2, 4, 5, 6, 8 ].filter(isEven)
[ 2, 4, 5, 6, 8 ].reduce(
(acc, val) => isEven(val) ? acc.push(val) : acc, acc, []);
이 경우에는 큰 장점이 없어보인다.
그렇지만 여기서 단순 필터링이 아닌 여러 조건별로 값을 변환하거나 수집하는 작업을 동시에 할 경우에는 reduce 함수가 꽤 가독성이 있고 성능도 좋개 구현할 수 있다.
아래 필터링과 값 매핑을 filter + map
하는 예제를 보자
// 첫번째 순회로 값을 필터링하고 나중에 곱하게 된다. 즉 두번의 루프를 돈다.
// 엔진별로 최적화(lazy evaluation) 를 할 수 있지만 그건 고려하지 않기로...
[ 2, 4, 5, 6, 8 ]
.filter(val => val%2 === 0)
.map(val => val * 10);
// 루프 한번에 filter, map 완료.
[ 2, 4, 5, 6, 8 ].reduce((acc, val) => {
if(val%2 === 0) {
acc.push(val * 10)
}
return acc;
}, []);
배열의 크기가 길수록 유리할 것이다.
callback 함수를 적절한 이름과 함께 분리해두면 더욱 가독성이 좋고 재활용의 여지도 생긴다.
타입변환
대상 배열의 타입을 오브젝트나 다른 타입으로 바꿀 수 있는 것도 reduce 의 특권이다.
find, filter, some 등의 메서드도 배열에서 다른 값을 반환하지만 반환 형식이 고정되어 있다.
그에 반해 reduce 는 자신이 선택한 타입을 반환하게 할 수 있다. 보통은 Object to Object, Array to Object 등으로 형식 변환시 주로 쓰게 된다.
다음 코드는 문자열 배열을 짝수 페어끼리 키와 값으로 매핑하는 예제이다.
const saids = [ 'ko', '안녕', 'en', 'hello', 'ja', 'こんにちは' ];
saids.reduce((acc, val, index, orig) =>
(index === 0 || index%2 === 0) ?
acc[val] = orig[index + 1] : acc, acc
}, {});
flatten
java 언어를 아시는 분이라면 stream 등으로 중첩된 스트림을 하나의 스트림으로 펴는 메서드인 flatMap
을 알 것이다.
js 에는 현재 공식으로 추가가 되어있지 않기에 직업 구현해야 한다. 이때 reduce
를 쓰면 간단히 구현된다.
[ [9,2], [8,7] ].reduce((acc, val) => [ ...acc, ...val ], [])
flatMap
은 현재 draft 상태인듯 하다. (https://tc39.github.io/proposal-flatMap/)
함수 결합
reduce
는 부분적용 혹은 커링 작업에도 큰 도움을 준다.
예를 들면 React(https://reactjs.org/) 를 쓰다보면 HOC(https://reactjs.org/docs/higher-order-components.html) 를 자주 쓰게 되는데 이런 경우 인라인으로 함수를 죽 statement 로 써내려가면 굉장히 보기 싫은 코드가 나온다.
이런 경우 reduce 로 미리 그런 함수를 합쳐둘 수 있다.
import React from 'react'
import { connect } from 'react-redux'
import injectStyles from 'jss'
import withHistory from 'react-router-dom'
import injectCopyButton from 'hoc/injectCopy'
import { selector, dispatcher }
const styles = {
// ... some implements
}
const MyComponent = props => (
// ... some implements
);
// 파동권 콜백 코드
// 많아질수록 더 괴로워질 것이다.
export default connect(selector, dispatcher)(
injectStyles(styles)(
withHistory(
injectCopyButton(
MyComponent
)
)
)
);
reduce
를 쓰면 이렇게 가독성을 높여볼 수 있다.
var hocs = () => {
return Component => {
return [
fn(1), fn(10),
].reduce((prev, fn) => fn(prev),Component)
}
};
const hocList = [
connect(selector, dispatcher),
injectStyles(styles),
withHistory,
injectCopyButton,
]
// HOC 배열을 주게 되면 제일 마지막 요소부터 적용해나가야 하기에 반전시킨다.
// 혹은 reduceRight 를 쓰는 방법도 있다.
// reduceRight 는 요소의 마지막부터 순회를 한다.
export default hocList.reverse().reduce((prev, hoc) => hoc(prev), Component);
조금 더 추상화하면 다음 함수를 만들 수 있다.
const compose = (...funcs) => val => funcs.reduce((pr, fn) => fn(pr), val);
forEach 로는 더 디테일하게 할수있는데?
reduce 보다 더 raw 한 성격의 함수로는 forEach(https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) 가 있다.
하지만 forEach
는 reduce
와 달리 반환값이 없기에 chaining method 방식으로는 쓸수 없어 활용성이 떨어지며, 보통 initialValue 가 별도의 공간이 선언하게 되어 변수 이름을 차지하게 되기에 reduce 가 사용성면에서 훨씬 유리하다.
@rouka 님 오셨군요 ㅋㅋㅋㅋ
가입인사글 부터 남기세요 !! kr-join kr-newbie 태그 ㄱㄱ
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Congratulations @rouka! You received a personal award!
Click here to view your Board
Do not miss the last post from @steemitboard:
Vote for @Steemitboard as a witness and get one more award and increased upvotes!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Congratulations @rouka! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Do not miss the last post from @steemitboard:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit