본문 바로가기

카테고리 없음

함수형 자바스크립트 공부 4. 맵, 필터, 리듀스

반응형

인프런 - 함수형 프로그래밍과 JavaScript ES6+ (inflearn.com)

 

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

ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,

www.inflearn.com

해당 강의 공부 중...!

맵, 필터, 리듀스

map

  const products = [
    {name: '반팔티', price: 15000},
    {name: '긴팔티', price: 20000},
    {name: '핸드폰케이스', price: 15000},
    {name: '후드티', price: 30000},
    {name: '바지', price: 25000}
  ];
// 여기서 네임과 프라이스만 뽑아내려면?

함수형 프로그래밍에서는 인자와 리턴값으로 소통하는 것을 권장함

(원래 녀석을 변화시키지 않음.)

const log = console.log;
const map = (f, iter) => {
    let res = [];
    for (const a of iter) {
        res.push(f(a));
    }
    return res; // iter를 리턴하지 않고 새로운 res를 만들어 리턴함
};

log(map(p => p.name, products)); // 네임 배열
log(map(p => p.price, products)); // 프라이스 배열
  • map 함수는 고차함수
    • 함수를 값으로 다루면서 원하는 순간에 인자를 적용함

이터러블 프로토콜을 따른 map의 다형성 1,2

  • 이터러블 프로토콜을 따르면 다형성이 매우 높음
log([1, 2, 3].map(a => a + 1)); // 가능
log(document.querySelectorAll('*').map(el => el.nodeName)) // 불가능
// 프로토타입에 map 메소드를 구현하지 않았음.
log(map(el => el.nodeName, document.querySelectorAll('*'))); // 위에서 만든 함수는 가능
// 이터레이터 프로토콜을 따르기 때문에 사용 가능
// const it = document.querySelectorAll('*')[Symbol.iterator]();
// log(it.next());
// log(it.next());
// log(it.next());
// log(it.next());
// log(it.next());

function* gen() {
    yield 2;
    if (false) yield 3;
    yield 4;
}
// 이터레이터 프로토콜만 따르면 모두 순회하도록 만들 수 있음
// 많은 헬퍼 함수들과의 조합성이 좋아진다.
log(map(a => a * a, gen()));

  • 다른 맵을 살펴보자
  • 맵 함수가 아닌 파이썬 딕셔너리와 비슷한 자료형
let m = new Map();
m.set('a', 10); 
m.set('b', 20);
const it  = m[Symbol.iterator](); // [['a',10],['b',20]]
map(([k, a]) => [k, a * 2], m)) // [['a',20],['b',40]]
log(new Map(map(([k, a]) => [k, a * 2], m))); // 안이 바뀐 맵 객체

filter

  • 특정 금액 이상, 이하만 걸러내기
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)); // {} 가격 20000미만
log(...filter(p => p.price >= 20000, products));  // {} 가격 20000이상
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

  • reduce는 iterable 값을 다른 값으로 축약해주는 함수
// 언제 필요할까?
const nums = [1, 2, 3, 4, 5];
let total = 0;
for (const n of nums) {
    total = total + n; // 값을 축약
}
log(total);

// 함수의 조합
const add = (a, b) => a + b;
log(add(add(add(add(add(0, 1), 2), 3), 4), 5));

// 함수 , 시작값, 이터러이터
const reduce = (f, acc, iter) => {
    if (!iter) { // 이터레이터가 아니면 ex 배열
        iter = acc[Symbol.iterator]();  // 이터레이터 객체를 꺼냄
        acc = iter.next().value; // 맨 처음 값을 가져옴
    }
    for (const a of iter) {
        acc = f(acc, a);
    }
    return acc;
};

// 보조함수를 통해 축약을 완전히 위임.
log(
    reduce(
        // iterator 안 객체인 product가 a에 들어가고 total_price가 acc에 들어감
        (total_price, product) => total_price + product.price,
        0,
products));

조합해서 사용해보기

const products = [
    {name: '반팔티', price: 15000},
    {name: '긴팔티', price: 20000},
    {name: '핸드폰케이스', price: 15000},
    {name: '후드티', price: 30000},
    {name: '바지', price: 25000}
];

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

// filter로 20000원 이하의 제품들을 필터링
// 객체 p 배열을 price 배열로 map
// add함수를 적용해서 reduce (총 가격 계산)
// filter map reduce
log(
    reduce(
        add,
        map(p => p.price,
            filter(p => p.price < 20000, products))));
// 20000 이상 filter map reduce
log(
    reduce(
        add,
        filter(n => n >= 20000,
               map(p => p.price, products))));
  • 함수를 조합해서 평가

  • 코드가 계산(Evaluation) 되어 값을 만드는 것

  • 맨 처음은 map을 통해 iterator로 뽑아올 값을 평가함

  • 그 다음 filter로 조건을 적용한다.

  • reducer로 값을 줄인다.

반응형