#1 map
이터러블 프로토콜을 따른 map
간단하게 먼저, map 함수는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다.
var array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
이제, 이터러블 프로토콜을 따른 map함수를 알아볼건데요, 먼저 map을 사용하지 않은 예제를 봅시다.
const products = [
{ name: '반팔티', price: 15000 },
{ name: '긴팔티', price: 20000 },
{ name: '후드티', price: 40000 },
{ name: '긴바지', price: 30000 },
{ name: '반바지', price: 25000 },
];
let names = [];
for (const p of products) {
names.push(p.name);
}
console.log(names);
//["반팔티", "긴팔티", "후드티", "긴바지", "반바지"]
let prices = [];
for (const p of products) {
prices.push(p.price);
}
console.log(prices);
//[15000, 20000, 40000, 30000, 25000]
다음은 위와같은 동작을 하는 map함수로 쓴 예제이다. map함수는 직접적으로 값을 수집하는 것이 아니라 추상적으로 값을 수집한다.
const products = [
{ name: '반팔티', price: 15000 },
{ name: '긴팔티', price: 20000 },
{ name: '후드티', price: 40000 },
{ name: '긴바지', price: 30000 },
{ name: '반바지', price: 25000 },
];
const map = (f, iter) => {
let res = [];
for(const a of iter) {
res.push(f(a));
}
return res;
};
console.log(map(p => p.name, products));
//["반팔티", "긴팔티", "후드티", "긴바지", "반바지"]
console.log(map(p => p.price, products));
//[15000, 20000, 40000, 30000, 25000]
다음은 map함수를 활용해 제너레이터와 함께 써봤다.
function* gen() {
yield 2;
yield 3;
yield 4;
}
console.log(map(a => a * a, gen()));
// [4, 9, 16]
결론적으로 map 함수는 array뿐만 아니라 이터러블 프로토콜을 따르는 많은 함수들을 다 사용 할 수 있다. 사실상 모든 것들을 map을 사용할 수 있다.
#2 filter
이터러블 프로토콜을 따른 filter
간단하게 먼저 filter는 조건에 맞는 내용만 리스트에 남겨두고 나머지 요소는 삭제하는 것이다.
아래코드는 filter를 사용하지 않은 코드이다. [ 가격이 3만원 이상, 미만인 제품 뽑기 ]
const products = [
{ name: '반팔티', price: 15000 },
{ name: '긴팔티', price: 20000 },
{ name: '후드티', price: 40000 },
{ name: '긴바지', price: 30000 },
{ name: '반바지', price: 25000 },
];
let under30000 = [];
for(const p of products) {
if(p.price < 30000) under30000.push(p);
}
console.log(...under30000);
// {name: "반팔티", price: 15000}
// {name: "긴팔티", price: 20000}
// {name: "반바지", price: 25000}
let over30000 = [];
for(const p of products) {
if(p.price >= 30000) over30000.push(p);
}
console.log(...over30000);
// {name: "후드티", price: 40000}
// {name: "긴바지", price: 30000}
위 코드를 filter로 재구성 해보자.
const products = [
{ name: '반팔티', price: 15000 },
{ name: '긴팔티', price: 20000 },
{ name: '후드티', price: 40000 },
{ 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;
};
console.log(...filter(p => p.price < 30000, products));
// {name: "반팔티", price: 15000}
// {name: "긴팔티", price: 20000}
// {name: "반바지", price: 25000}
console.log(...filter(p => p.price >= 30000, products));
// {name: "후드티", price: 40000}
// {name: "긴바지", price: 30000}
filter 역시 중복된 요소를 제거해서 이러터블 프로토콜을 사용함을 알 수 있다.
다음은 filter함수를 활용해 제너라이트와 함께 써봤다.
console.log(filter(n => n % 2, function *() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
}
//[1, 3, 5]
#3 reduce
이터러블 프로토콜을 따른 filter
간단하게 먼저 reduce는 하나의 다른 값으로 축약하는 함수이다. 아래의 예제는 reduce가 동작하는 코드를 구성해본 것이다.
const nums = [1, 2, 3, 4, 5];
let total = 0;
for (const n of nums) {
total = total + n;
}
console.log(total); // 15
위의 reduce 코드 역시 이러터블 프로토콜을 따르게끔 재구성 해보았다.
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;
};
const add = (a, b) => a + b;
console.log(reduce(add, 0, [1, 2, 3, 4, 5])); // 15
console.log(reduce(add, [1, 2, 3, 4, 5])); // 15 (!iter이더라도 같은 동작을 함)
이제 products의 모든 금액들을 더하는 것을 reduce만으로 작성해보자.
const products = [
{ name: '반팔티', price: 15000 },
{ name: '긴팔티', price: 20000 },
{ name: '후드티', price: 40000 },
{ 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));
//130000
#4 map+filter+reduce 중첩
map+filter+reduce 중첩 사용과 함수형 사고
이 챕터에선 위에서 이터러블을 따른 함수들을 전부 같이 사용할 것이다. 아래의 코드를 보자.
[ 3만원 미만의 제품의 금액의 합을 나타내기]
/*fx.js*/
const map = (f, iter) => {
let res = [];
for(const a of iter) {
res.push(f(a));
}
return res;
};
const filter = (f, iter) => {
let res = [];
for(const a of iter) {
if(f(a)) res.push(a);
}
return res;
};
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;
};
/*src="fx.js"*/
const products = [
{ name: '반팔티', price: 15000 },
{ name: '긴팔티', price: 20000 },
{ name: '후드티', price: 40000 },
{ name: '긴바지', price: 30000 },
{ name: '반바지', price: 25000 },
];
const add = (a, b) => a + b;
//console.log(map(p => p.price, products));
//console.log(map(p => p.price, filter(p => p.price < 30000, products)));
console.log(reduce(
add,
map(p => p.price,
filter(p => p.price < 30000, products)))); // 60000
//3만원 미만의 제품을 filter로 뽑고, 그 값들을 map을 통해 뽑아낸 후,
//해당하는 모든값을 reduce로 축약해서 나타냄
위의 코드를 작성할 때는 reduce부터 filter까지 작성 하고 해석할 때는, filter부터 reduce 순으로 이해하는 것이 편하다.
'Web[웹] > ES6+ 함수형 언어' 카테고리의 다른 글
[JS: ES6+] 제너레이터/이터레이터 프로토콜로 구현하는 지연 평가 (1) (0) | 2019.10.08 |
---|---|
[JS: ES6+] 장바구니 예제로 코드를 줄이고 HTML로 표현해보기 (0) | 2019.10.07 |
[JS: ES6+] 코드를 값으로 다루어 표현력 높이기 (go, pipe) (0) | 2019.10.04 |
[JS: ES6+] 제너레이터와 이터레이터 (0) | 2019.09.30 |
[JS: ES6+] ES6에서의 순회와 이터러블:이터레이터 프로토콜 (0) | 2019.09.30 |