Skip to content
Underbleu
GithubLinkedin

2-2. 컬렉션 중심 프로그래밍 - reduce 활용 (posts, users), countBy, groupBy

Functional programming1 min read

프로그래머스에서 진행한 유인동님의 ES6로 알아보는 동시성 & 함수형 프로그래밍 강의를 들으며 정리한 내용입니다.

# reverseIter()

배열의 값을 거꾸로 순회하며 값을 리턴해주는 제너레이터 함수

1function *reverseIter(arr) {
2 let l = arr.length
3 while(l--) yield arr[l]
4}
5
6log(...reverseIter([1, 2, 3])) // 3 2 1

# reduce() - acc 있는 경우 vs 없는 경우

reduce함수를 쓰는 목적: 인자로 받은 컬렉션을 시작부터 끝까지 순회하며 무언가를 만들 때 사용

  1. acc 없는 경우: 반드시 acc와 a가 같은 형이라는걸 보장할 수 있을 때 사용
  2. acc 있는 경우: acc와 a가 다른 형이어도 된다
    다만 형이 다를경우, 보조함수의 리턴값이 acc가 되도록 해줘야 함
1// 1. acc 없는 경우
2reduce((acc, a) => acc + a, [1,2,3]); // 6
3
4// 2. acc 있는 경우
5/* acc.value += a -> acc에 a를 누적해주고
6 * acc -> 리턴 */
7reduce((acc, a) => (acc.value += a, acc), [1,2,3], {value: 0}); // 6

() => (a, b) 뒤의 값(b)이 리턴되는 패턴을 활용

1a = 10, 20 // 20 -> 뒤의 값이 리턴됨
2a // 10
3
4var a = 10, b // a와 b가 함께 선언되는것
5a // 10
6b // undefined

# reduce() 활용하기

1. 보조함수를 통해 다양한 기능 수행

1const posts = [
2 {id: 1, body: "내용1", comments: [{}]},
3 {id: 2, body: "내용2", comments: [{}, {}, {}]},
4 {id: 3, body: "내용3", comments: [{}, {}]},
5 {id: 4, body: "내용4", comments: [{}]},
6];
7
8const users = [
9 {id: 1, name: "name1", age: 21},
10 {id: 2, name: "name2", age: 23},
11 {id: 3, name: "name3", age: 20},
12 {id: 4, name: "name4", age: 23},
13 {id: 5, name: "name5", age: 23},
14 {id: 6, name: "name6", age: 21}
15];
16
17// posts의 comments의 총 수
18reduce((count, p)=> (count += p.comments.length, count), posts, 0);
19
20// age별 user 그룹
21reduce((group, u) => {
22 (group[u.age] || (group[u.age] = [])).push(u);
23 return group;
24}, users, {});
25
26// age별 user의 수
27reduce((count, u) =>
28 (count[u.age] ? count[u.age]++ : count[u.age] = 1, count)
29, users, {});

2. 보조함수 incSel(), pushSel()를 생성하여 표현을 간결하게 만들어 준다

1function incSel(parent, k) {
2 parent[k] ? parent[k]++ : parent[k] = 1;
3 return parent;
4}
5
6function pushSel(parent, k, v) {
7 (parent[k] || (parent[k] = [])).push(v)
8 return parent;
9}
10
11reduce((count, u) => incSel(count, u.age), users, {});
12// {20: 1, 21: 2, 23: 3}
13
14reduce((group, u) => pushSel(group, u.age, u), users, {});
15// {20: Array(1), 21: Array(2), 23: Array(3)}

3. 추상도를 더 높인 countBy(), groupBy() 함수

위의 함수에서 가변적인 부분은 u.age뿐이다. 추상도를 더 높여 다방면으로 활용가능한 함수를 만들어보자

1const countBy = (f, coll) => reduce((count, a) => incSel(count, f(a)), coll, {});
2const groupBy = (f, coll) => reduce((group, a) => pushSel(group, f(a), a), coll, {});
3
4countBy(u => u.age, users); // {20: 1, 21: 2, 23: 3}
5groupBy(u => u.age, users); // {20: Arr(1), 21: Arr(2), 23: Arr(3)}

ex) countBy()로 아래의 결과들을 집계할 수 있다

  • a로부터 연산한 결과
  • a로부터 접근 .get() 결과
  • a의 메서드를 실행했을 때의 결과
1// 다방면으로 응용
2countBy(n => n, [1,1,2,3,3,3]); // {1: 2, 2: 1, 3: 3}
3groupBy(n => n, [1,1,2,3,3,3]); // {1: [1,1], 2: [2], 3: [3,3,3]}
4
5const identity = a => a;
6const count = data => countBy(identity, data);
7
8count([1,1,2,3,3,3]); // {1: 2, 2: 1, 3: 3}

# 인자 이름 arr, coll의 차이 ?

  • arr은 형이 반드시 배열일때만 인자이름으로 사용하도록
  • coll은 [Symbol.iterator] 이름의 메서드를 가진 순회가능한 모든 컬렉션객체