-
[JS] Map, Filter, Reduce기초/자바스크립트 2022. 12. 17. 02:53
인프런 강의 정리
함수형 프로그래밍과 Javascript ES6+
*custom Map 함수
/** f : 맵 속성을 위임할 함수 iter : 이터러블을 반환하는 제너레이터 function : 이터러블을 받아 맵을 생성하는 함수 **/ const map = (f, iter) => { let res = []; for (const a of iter) { res.push(f(a)); } return res; };
*원하는 value만 맵으로 생성
console.log(map(p => p.name, products)); console.log(map(p => p.price, products)); /** 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); **/
이터러블 프로토콜을 따른 map의 다형성
1. 기존 이터러블을 이용한 맵 객체 생성
/** document.querySelectorAll('*')[Symbol.iterator](); //Array Iterator {} **/ console.log([1, 2, 3].map(a => a + 1)); // [2, 3, 4] console.log(map(el => el.nodeName, document.querySelectorAll('*'))); // [ tag list ... ] function* gen() { yield 2; if (false) yield 3; yield 4; } console.log(map(a => a * a, gen())); // [4, 16]
2. 기존 맵을 이용한 맵 객체 생성
- 맵의 값을 변경하여 사용할 수 있다.
let m = new Map(); m.set('a', 10); m.set('b', 20); console.log(new Map(map(([k, a]) => [k, a * 2], m))); //Map(2) {'a' => 20, 'b' => 40}
*Filter
조건에 맞는 Value만 맵으로 생성
/** f : 조건 iter : 이터러블 function : 조건에 맞는 이터러블 집합을 맵으로 생성 **/ const filter = (f, iter) => { let res = []; for (const a of iter) { if (f(a)) res.push(a); } return res; };
const products = [ {name: '짜장', price: 10000}, {name: '짬뽕', price: 12000}, {name: '볶음밥', price: 15000}, {name: '탕수육', price: 25000} ]; // 원하는 결과만 가져오기 console.log(...filter(p => p.price < 20000, products)); // {name: '짜장', price: 10000} {name: '짬뽕', price: 12000} {name: '볶음밥', price: 15000} console.log(...filter(p => p.price >= 20000, products)); // {name: '탕수육', price: 25000} console.log(filter(n => n % 2, [1, 2, 3, 4])); // [1, 3] // 제너레이터를 바로 사용할 수 있다. console.log(filter(n => n % 2, function* () { yield 1; yield 2; yield 3; }())); // [1, 3]
*reduce
집합을 요약
/** f : 연산함수 acc : 집합의 초기값 iter : 집합 function : 집합을 연산함수에 따라 요약하는 함수 **/ const reduce = (f, acc, iter) => { //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(add(add(add(add(add(0, 1), 2), 3), 4), 5)); // 15 console.log(reduce(add, [1, 2, 3, 4, 5])); // 15
-사용2
-이터러블 함수들을 다양하게 사용
const products = [ {name: '짜장', price: 10000}, {name: '짬뽕', price: 12000}, {name: '볶음밥', price: 15000}, {name: '탕수육', price: 25000} ]; const add = (a, b) => a + b; 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; }; /** reduce, add, map > filter **/ console.log( reduce( add, map(p => p.price, filter(p => p.price < 20000, products)))); //37000 /** reduce, add, filter > map **/ console.log( reduce( add, filter(n => n >= 20000, map(p => p.price, products)))); //25000
읽기 좋은 코드로 변경하는 방법
1. 값과 함수를 반환하는 함수 만들기
/** (a, f) : 값과 함수로 이뤄진 파라매터 리스트 args : 파라매터 리스트 go : 순차적으로 함수를 처리해 값을 반환하는 함수 첫번째 값 >> 두번째 함수의 파라매터 두번째 함수의 결과값 >> 세번째 함수의 파라매터 ... **/ const go = (...args) => reduce((a, f) => f(a), args); /** (...as) : 아규먼트 리스트 f : 첫번째 함수 fs : 함수리스트 pipe : 내부적으로 go를 사용하는 함수 리스트 **/ const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
/** 사용2 함수 console.log( reduce( add, map(p => p.price, filter(p => p.price < 20000, products)))); //37000 **/ /** 위의 함수를 더 간편하게 보기위해 go함수를 사용한 리펙토링 **/ go( products, products => filter(p => p.price < 20000, products), products => map(p => p.price, products), prices => reduce(add, prices), console.log);
#curry
/** f = (a, ..._) 함수를 받아서 함수를 리턴 f(a, ..._) : 리턴된 함수가 2개 이상일 때 즉시 실행 (..._) => f(a, ..._) : 리턴된 함수가 2개 미만일 때 인자를 받도록 대기하는 함수를 리턴하고 이 후에 받은 함수와 합쳐서 실행 **/ const curry = f => (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
- filter, map, reduce함수 에 curry함수를 적용
-기존 map, reduce, filter
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) => { //iter가 생략된 경우 if (!iter) { iter = acc[Symbol.iterator](); acc = iter.next().value; } for (const a of iter) { acc = f(acc, a); } return acc; };
- curry가 적용된 함수
const map = curry((f, iter) => { let res = []; for (const a of iter) { res.push(f(a)); } return res; }); const filter = curry((f, iter) => { let res = []; for (const a of iter) { if (f(a)) res.push(a); } return res; }); const reduce = curry((f, acc, iter) => { if (!iter) { iter = acc[Symbol.iterator](); acc = iter.next().value; } for (const a of iter) { acc = f(acc, a); } return acc; });
-적용된 함수
/** 위의 함수를 더 간편하게 보기위해 go함수를 사용한 리펙토링 go( products, products => filter(p => p.price < 20000, products), products => map(p => p.price, products), prices => reduce(add, prices), console.log); **/ go( products, filter(p => p.price < 20000), map(p => p.price), reduce(add), console.log);
-pipe 함수를 사용해 공통부분 추출 및 함수 의미부여
const total_price = pipe( map(p => p.price), reduce(add)); // (...as) => go(f(...as), ...fs) const base_total_price = predi => pipe( filter(predi), total_price); /** console.log(base_total_price(p => p.price < 20000)(products)) **/ go( products, base_total_price(p => p.price < 20000), // (...as) => go(f(...as), ...fs) console.log); // 37000
'기초 > 자바스크립트' 카테고리의 다른 글
[JS] 제너레이터 (0) 2022.12.17 [JS] 일급 함수와 고차함수 (0) 2022.12.14 GridSatck (Test.html) (0) 2022.03.16 [JS] 모자이크 처리 (2) 2022.03.04 [Javascript] OpenLayers + GeoServer + PostGIS + VWORLD + WFS,WMS (7) 2020.11.11