함수형 프로그래밍 4 map,filter,reduce

map

아래의 함수는 이터러블에 사용 가능하게 작성된 map 함수이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const products = [
{ name: "반팔티", price: 15000 },
{ name: "긴팔티", price: 20000 },
{ name: "핸드폰케이스", price: 15000 },
{ name: "후드티", price: 30000 },
{ name: "바지", price: 25000 },
];

const map = (f, iter) => {
// f를 통해 어떤 값을 반환할지 정한다.
let res = [];

for (const p of iter) {
res.push(f(p));
}

return res;
};

console.log(map((p) => p.name, products));
// ['반팔티','긴팔티','핸드폰케이스','후드티','바지']

console.log(map((p) => p.price, products));
// [15000,20000,15000,30000,25000]

filter

아래의 함수는 이터러블에 사용 가능하게 작성된 filter 함수이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const products = [
{ name: "반팔티", price: 15000 },
{ name: "긴팔티", price: 20000 },
{ name: "핸드폰케이스", price: 15000 },
{ name: "후드티", price: 30000 },
{ name: "바지", price: 25000 },
];

const filter = (f, iter) => {
let res = [];
for (const a of iter) {
if (f(a)) res.push(a);
}
return res;
};

log(...filter((p) => p.price < 20000, products));
// [{name:'반팔티',price:15000},,{name:'핸드폰케이스',price:15000}]

console.log(filter((p) => p.price >= 20000, products));
// [{name:'긴팔티',price:20000},{name:'후드티',price:30000},{name:'바지',price:25000}]

log(filter((n) => n % 2, [1, 2, 3, 4]));
// [1, 3];

log(
filter(
(n) => n % 2,
(function* () {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
})()
)
);
// [1,3,5]

reduce

아래의 함수는 이터러블에 사용 가능하게 작성된 filter 함수이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const nums = [1, 2, 3, 4, 5];

const reduce = (f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
// parameter가 2 개만 들어왔을 경우 iter가 없는 경우이다.
// 이 때는 자기 자신을 반환하는 iterable의 성질을 이용해서 iter를 만들어준다.
for (const a of iter) {
acc = f(acc, a);
}
return acc;
};

const add = (a, b) => a + b;

console.log(reduce(add, 0, [1, 2, 3, 4, 5]));
// 15

console.log(add(add(add(add(add(0, 1), 2), 3), 4), 5));
// 15

console.log(reduce(add, [1, 2, 3, 4, 5]));
// == console.log(reduce(add, 1, [2, 3, 4, 5]))
// 15

단순 숫자 계산이 아니여도 활용할 수 있다.
아래는 products배열의 price를 더하는 코드이다.(다형성)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const products = [
{ name: "반팔티", price: 15000 },
{ name: "긴팔티", price: 20000 },
{ name: "핸드폰케이스", price: 15000 },
{ name: "후드티", price: 30000 },
{ name: "바지", price: 25000 },
];

const reduce = (f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter) {
acc = f(acc, a);
}
return acc;
};

console.log(
reduce((total_price, product) => total_price + product.price, 0, products)
);
// 105000

다형성

위에서 작성한 map,filter,reduce함수는 이터러블 프로토콜을 따르는 객체에 전부 적용할 수 있다.
관련된 연산자도 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const map = (f, iter) => {
let res = [];
for (const p of iter) {
res.push(f(p));
}
return res;
};
console.log(document.querySelectorAll("*").map((ele) => ele.nodeName));
// 배열이 아니기 때문에 에러가 발생한다.
console.log(map((el) => el.nodeName, document.querySelectorAll("*")));
// nodeList는 이터러블 프로토콜을 따르기 때문에 동작한다.

function* gen() {
yield 2;
yield 3;
yield 4;
}

console.log(
map((a) => a * a),
gen()
);
// 제너레이터에도 동작한다
// [4,9,16]

제너레이터 또한 이터러블 프로토콜을 따르기 때문에 제너레이터에도 적용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const map = (f, iter) => {
let res = [];
for (const p of iter) {
res.push(f(p));
}
return res;
};

function* gen() {
yield 2;
yield 3;
yield 4;
}

console.log(
map((a) => a * a),
gen()
);
// 제너레이터에도 동작한다
// [4,9,16]

마찬가지로 map과 set에도 적용할 수 있다.

1
2
3
4
5
let m = new Map();
m.set("a", 10);
m.set("b", 20);
console.log(new Map(map(([k, a]) => [k, a * 2], m)));
// [[a,20],[b,40]]

Ref

함수형 프로그래밍과 JavaScript ES6+

댓글