본문 바로가기

Javascript

map + filter + reduce 중첩 사용과 함수형 사고 - JS ES6+ 함수형 프로그래밍 - 17

본 시리즈는 인프런 강의 함수형 프로그래밍과 JavaScript ES6+ ( 지식 공유자 : 유인동 님 ) 의 강의를 수강하면서 내용을 제 방식대로 포스팅하는 글입니다.

 

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

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

www.inflearn.com

이번에는 앞에서 만들었던 map, filter, reduce 함수를 함께 중첩해서 사용해 보는 코드를 작성해보자

앞의 내용을 건너뛰고 오신분들을 위해, 먼저 함수를 모듈화해서 정리해보겠습니다. ( 사실 내가 정리 해야해서,,, )

export function map(fn: (elem: any) => any, iter: Iterable<any>) {
  let res = [];

  for (const a of iter) {
    res.push(fn(a));
  }

  return res;
}

export function filter(fn: (elem: any) => any, iter: Iterable<any>) {
  let res = [];

  for (const a of iter) {
    if (fn(a)) res.push(a);
  }

  return res;
}

export function reduce(
  fn: (result: any, data: any) => any,
  acc: number | Iterable<any>,
  iter?: Iterable<any> | Iterator<any>
) {
  if (!iter) {
    acc = acc as Iterable<any>;
    iter = acc[Symbol.iterator]();
    acc = iter.next().value;
  }

  for (const a of iter as Iterable<any>) {
    acc = fn(acc, a);
  }

  return acc;
}

이제 데이터를 다뤄보며 응용해보자.

- 특정 금액 이하의 가격들을 뽑아서, 이 가격을 모두 합친 금액이 얼마인지 계산해보자.

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

  // 특정 금액 이하의 가격들을 뽑아서, 이 가격을 모두 합친 금액이 얼마인지 계산해보자.

  // reduce 가 사용하는 보조함수 add()
  const add = (a: number, b: number) => a + b;

  const filterResult_1 = filter((product) => product.price < 20000, products); // [{ name: "반팔티", price: 15000 }, { name: "핸드폰케이스", price: 15000 }]
  const mapResult_1 = map((product) => product.price, filterResult_1); // [15000, 15000]
  const reduceResult_1 = reduce(add, mapResult_1); // 30000

  const mapResult_2 = map((product) => product.price, products); // [15000, 20000, 15000, 30000, 25000]
  const filterResult_2 = filter((price) => price < 20000, mapResult_2); // [15000, 15000]
  const reduceResult_2 = reduce(add, filterResult_2); // 30000

  // 위 아래의 결과가 동일한 30000원이 된다.
  log(reduceResult_1);
  log(reduceResult_2);

위의 코드를 연속해서 함수를 사용하는 형태로 작성하면 다음과 같다.

// 맨 안쪽 끝부터 읽기 시작하면 좀더 읽기 좋다.

log(
    reduce(
      add,
      map(
        (product) => product.price,
        filter((product) => product.price < 20000, products)
      )
    )
  );

  log(
    reduce(
      add,
      filter(
        (price) => price < 20000,
        map((product) => product.price, products)
      )
    )
  );

함수형 프로그래밍 에서는 함수들을 중첩하고 연속해서 실행하면서 어떤 하나의 값으로 부터 출발해서 원하는 값으로 만들어 가는 형태로 사고를 하면 된다.

예를들어, 위의 상황에서 어떠한 데이터를 add 를 통해 reduce 를 하고 싶다면, 

// 아래와 같은 형태까지는 별생각 없이 작성할수 있고
// log(reduce(add,
  
//   ));

// 이 빈자리의 코드가 평가 되었을때 숫자가 있는 배열로 평가가 될것을 기대하면서
// reduce 와 add 를 작성하면, 잘 동작할 준비가 된 코드를 이미 작성할수 있다.

// 따라서 아래 배열 자리에 내가 어떠한 코드를 작성해야 한다고 생각하고 접근할수 있다.
log(reduce(add, [10, 20, 30, 40])); // 100

// map 을 통해서 숫자가 있는 배열이 평가 될것으로 기대하고 아래와 같이 작성할수 있다.
// reduce 또한 숫자 값으로 평가될것을 기대할수 있다.
log(reduce(add, map(product => product.price, products))); // === log(105000)

// 또한 products 역시 map 함수에서 추출 되기 전에 이자리에 들어올 데이터들을
// 특정 조건의 products 들만 평가되어져 있는 배열로 코드를 작성하면 되겠다 생각할수 있다.
  log(
    reduce(
      add,
      map((product) => product.price, products)
    )
  );
  
// 예를 들면 위의 products 배열에 아래와 같이 특정한 배열만 들어오게끔 하려면

  log(
    reduce(
      add,
      map(
        (product) => product.price,
        
        // products 자리에 아래와 같은 값이 평가되도록 기대하고, 
        // 이런 값이 평가될 코드를 작성한다고 생각하고 접근한다.
        [
          { name: "반팔티", price: 15000 },
          { name: "핸드폰케이스", price: 15000 },
        ]
      )
    )
  );
  
  log(
    reduce(
      add,
      map(
        (product) => product.price,
        filter((product) => product.price < 20000, products)
      )
    )
  ); // 30000
  
  // filter 함수를 통해 기대하는 값을 평가해낼수 있다.

함수형 프로그래밍에서는 이런식으로 사고를 해가면서 프로그래밍을 한다.