JS - 함수형 프로그래밍 - 2

sarang_daddy·2023년 2월 4일
0

Javascript

목록 보기
13/26
post-thumbnail
post-custom-banner

앞서 학습한 함수형 프로그래밍 - 1의 연습을 더 해보자.

기능 요구사항

  • 아래 요구사항을 만족하는 두 개의 기능을 구현한다.
  • 중복되는 코드는 줄이거나 불변성, 참조투명성, 순수함수로 동작하는 함수형 표현으로 선언적으로 개선한다.

프로그래밍 요구사항

  • 집합을 구현할 때 Array 혹은 List 배열만 사용하며, Object, HashMap이나 기존에 구현된 Set, HashSet 등 라이브러리는 사용할 수 없다.
  • 핵심 로직 대신 입력이나 출력용으로 Object나 Map 구조로 값을 전달하는 것은 가능하다.

1. SquadSet

  • 불변 타입으로 초기화할 때만 배열로 값을 넘길 수 있다. 추가나 삭제는 되지 않는다.
  • 수학에서 집합 개념을 지원하여, 요소가 중복되서는 안된다.
  • sum(other) : SquadSet에 다른other SquadSet 요소들을 더해서 합집합을 리턴한다. 이미 같은 값이 있으면 추가하지 않는다.
  • complement(other) : SquadSet에서 다른other SquadSet 요소를 빼서 차집합을 리턴한다. 값이 포함되어 있지 않으면 아무런 변화도 없다.
  • intersect(other) : SquadSet와 다른other SquadSet 값과 비교해서, 두 집합에 모두 있는 원소 - 교집합을 리턴한다.
  • resultAll() : 모든 요소를 1차원 배열로 리턴한다.
// 입력값 array를 SquadSet집합(중복값제거)으로 만들어 주는 함수
const makeSuqadSet = (array) => array.filter((v, i) => array.indexOf(v) === i);

// 집합 A와 B의 합집합 리턴 함수
const sum = (A, B) => A.concat(B).filter((v, i) => A.indexOf(v) === i);

// 집합 A와 B의 차집합 리턴 함수
const complement = (A, B) => A.filter((v) => !B.includes(v));

// 집합 A와 B의 교집합 리턴 함수
const intersection = (A, B) => A.filter((v) => B.includes(v));

const resultAll = () => {
  const setA = makeSuqadSet([1, 1, 2, 2, 2, 3]);
  const setB = makeSuqadSet([1, 3]);
  intersection(setA, setB);
  sum(setA, setB);
  complement(setA, setB);

  console.log(
    `A 집합 = [${setA}]\nB 집합 = [${setB}]\n합집합 = [${sum(
      setA,
      setB
    )}]\n차집합 = [${complement(setA, setB)}]\n교집합 = [${intersection(
      setA,
      setB
    )}]`
  );
};

resultAll();

SquadSet 출력

2 CountSet

  • 불변 타입으로 초기화할 때 Object 또는 HashMap으로 값을 넘길 수 있다.
  • 새로운 요소를 추가하거나 삭제하면 새로운 CountSet를 리턴한다.
  • SquadSet과 달리 요소가 중복해서 있을 수 있고, 요소별 Count 값을 가지고 있다.
  • append(elements) : 새로운 요소를 추가하고 새로운 CountSet을 리턴한다. 이미 있는 경우는 Count만 증가하고 리턴한다.
  • remove(elements) : 기존에 요소가 있으면 Count를 줄인다. 만약 0이되면 제거한 CountSet을 리턴한다.
  • countFor(element) : 특정 요소에 대한 Count 값을 리턴한다.
  • sum(other) : CountSet에 다른other CountSet 요소들을 더해서 합집합을 리턴한다. 이미 같은 값이 있으면 합쳐서 카운트를 올린다.
  • complement(other) : CountSet에서 다른other CountSet 요소를 빼서 차집합을 리턴한다. 값이 포함되어 있지 않으면 아무런 변화도 없다. 만약 현재 CountSet보다 빼려는 other CountSet 요소 Count가 더 큰 경우는 제거한다. (Count는 마이너스가 되지 않고 0보다 같거나 작으면 제거한다.)
  • intersect(other) : Set와 다른other CountSet 값과 비교해서, 두 집합에 모두 있는 원소 - 교집합을 리턴한다. 교집합 Count는 모두 1로 리턴한다.
  • resultAll() : 모든 요소와 Count를 Object 형태로 리턴한다.
// 입력값 Array를 countSet집합({key : value})으로 만드는 함수
const makeCountSet = (array) =>
  array.reduce((acc, v) => {
    acc[v] = (acc[v] || 0) + 1;
    return acc;
  }, {});

// 객체 형태의 countSet을 배열로 만드는 함수
const objectToArray = (object) =>
  Object.entries(object).flatMap(([key, value]) =>
    Array(value).fill(Number(key))
  );

// countSet에 {key : value} 요소를 추가하는 함수
const append = (A, elements) => makeCountSet(A.concat(objectToArray(elements)));

// countSet에 {key : value} 요소를 제거하는 함수
const remove = (A, elements) => {
  const removedA = [...A];
  objectToArray(elements).map((v) => {
    if (removedA.indexOf(v) > -1) {
      removedA.splice(removedA.indexOf(v), 1);
    }
  });
  return makeCountSet(removedA);
};

// countSet의 특정 요소 count를 반환하는 함수
const countFor = (A, element) => makeCountSet(A)[element];

// countSetA 와 counstSetB의 합집합
const sum = (A, B) => makeCountSet(A.concat(B));

// countSetA 와 counstSetB의 차집합
const complement = (A, B) => {
  const complementA = [...A];
  B.map((v) => {
    if (complementA.indexOf(v) > -1) {
      complementA.splice(complementA.indexOf(v), 1);
    }
  });
  return makeCountSet(complementA);
};

// countSetA 와 counstSetB의 교집합
const intersection = (A, B) =>
  makeCountSet(
    A.filter((v) => B.includes(v)).reduce(
      (acc, v) => (acc.includes(v) ? acc : [...acc, v]),
      []
    )
  );

const resultAll = () => {
  const counstSetA = [1, 1, 2, 2, 3, 3];
  const counstSetB = [1, 3, 3, 3];

  console.log(
    `A Count집합 = ${JSON.stringify(
      makeCountSet(counstSetA)
    )}\nB Count집합 = ${JSON.stringify(
      makeCountSet(counstSetB)
    )}\n합집함sum = ${JSON.stringify(
      sum(counstSetA, counstSetB)
    )}\n차집합complement = ${JSON.stringify(
      complement(counstSetA, counstSetB)
    )}\n교집합intersect = ${JSON.stringify(
      intersection(counstSetA, counstSetB)
    )}\nAppend { 1: 2, 4: 1 } to A = ${JSON.stringify(
      append(counstSetA, { 1: 2, 4: 1 })
    )}\nremove { 1: 2, 4: 1 } to A = ${JSON.stringify(
      remove(counstSetA, { 1: 2, 4: 1 })
    )}\ncountFor "key : 1" of A = ${JSON.stringify(countFor(counstSetA, 1))}`
  );
};

resultAll();

CountSet 출력

참고자료

지연평가
배열을 객체로
배열 중복 값 개수
불변객체
객체에 key value
reduce의 활용
커링강의
뷸변성 객체
this강의

profile
한 발자국, 한 걸음 느리더라도 하루하루 발전하는 삶을 살자.
post-custom-banner

0개의 댓글