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

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

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

코드를 값으로 다루어 표현력 높이기

[go, pipe]

const go = (...args) => reduce((a, f) => f(a), args);
// 리스트로 받는다
// 인자를 연속적으로 전달 -> 리듀스 라는 이야기
// args를 하나의 값으로 축약하는 과정

// 연산이 연속적으로 일어나서 하나의 값을 만드는 함수
go(
  add(0, 1), // 첫번째 인자는 값
  a => a + 10,
  a => a + 100,
  log);
// 111
const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
// 함수를 값으로 하기 때문에 내부에서 go를 사용
// ...as 는 축약된 함수, 실행되는 첫번째 함수가 인자를 여러개 받기 위해서 전개를 함
// 여러개!!

// 합성된 축약된 함수를 만드는 함수
const f = pipe(
  (a, b) => a + b,
  a => a + 10,
  a => a + 100);

log(f(0, 1));

console.clear();

[위 함수 이용해서 개선]

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

// go 함수 이용해서 순서를 더 명확하게 뒤집음
go(
  products,
  products => filter(p => p.price < 20000, products),
  products => map(p => p.price, products),
  prices => reduce(add, prices),
  log);

[curry]

const curry = f => (a,..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
// 2개 이상일때가 원하는 시점
// 인자가 2개 이상이면 해당 함수를 실행하고
// 아니면 인자를 더 받아야하는 함수 리턴하고 나중에 받은 인자까지 합쳐서 실행하는 함수

const mult = curry((a, b) => a * b);
// 함수를 일단 등록

// case1 두개 이상 등록시
log(mult(3, 2)); // 6

// case2 하나만 등록시
const mult3 = mult(3); // 3이 미리 들어 가있고 추가 인자 전달시 값이 나오는 함수
log(mult3(10));
log(mult3(5));
log(mult3(3));
console.clear();

[Curry 적용으로 개선]

const log = console.log;

const curry = f =>
  (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);

// map filter reduce 함수의 경우 해당 구조로 감싸게 되면
// map filter reduce 함수가 먼저 등록되고
// 조건 함수가 등록될 때 인자가 1개기 때문에 또 다시 인자를 받는 함수가 반환이 되기 때문에 유용하다
// 조금 더 간결해진다

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;
});

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

const enrolledMap = map(p => p.price);
console.log(enrolledMap); // 함수
console.log(enrolledMap(products)); // [15000, 20000, 15000, 30000, 25000]
log(
  reduce(
    add,
    map(p => p.price,
      filter(p => p.price < 20000, products))));

// 1단계 go만 적용 했을때
go(
  products,
  products => filter(p => p.price < 20000, products),
  products => map(p => p.price, products),
  prices => reduce(add, prices),
  log);

// 2단계 curry 까지 적용 했을때

go(
  products,
  products => filter(p => p.price < 20000)(products),
	// filter(p => p.price < 20000)는 일단 함수를 받고 기다릴 수 있다

	// products => 실행함수(products)는 어짜피 go 안의 reduce안에서 축약된다
	// 실행 함수만만 제대로 전달이 되면 된다

  products => map(p => p.price)(products),
  prices => reduce(add)(prices),
  log);

go(
  products,
  filter(p => p.price < 20000),
	// 인자를 하나만 받아서 반환 값이 조건함수 물고있는 함수
  map(p => p.price),
  reduce(add),
  log);