[PS] 프로그래머스 - 표 병합 (JavaScript)

olwooz·2023년 1월 8일
0

PS

목록 보기
2/2

표 병합

문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/150366

접근 방식

MERGE 기능으로 인해 특정 셀에 업데이트가 발생하면 병합된 모든 셀에 반영해줘야 하기 때문에 Union-find로 풀면 좋을 것이라 판단했다.

구현해야 할 기능이 많기 때문에 solution 함수 내부에 기능별로 중첩 함수를 구현하고 commands를 순회하면서 전역 변수 cellparent를 업데이트해주는 식으로 풀었다.

function solution(commands) {
  let answer = [];
  const cell = Array(51)
    .fill()
    .map((_) => Array(51).fill(''));
  const parent = Array(51)
    .fill()
    .map((_, i) =>
      Array(51)
        .fill()
        .map((_, j) => [i, j])
    );

  for (const commandStr of commands) {
    const [command, ...rest] = commandStr.split(' ');

    switch (command) {
      case 'UPDATE':
        if (rest.length === 3) update(rest);
        else replace(rest);
        break;
      case 'MERGE':
        merge(rest);
        break;
      case 'UNMERGE':
        unmerge(rest);
        break;
      case 'PRINT':
        answer = [...answer, print(rest)];
        break;
    }
  }

  return answer;

  function updateAll(target, value, type) {
    const _target = target.toString();

    for (let i = 0; i < 51; i++) {
      for (let j = 0; j < 51; j++) {
        if (parent[i][j].toString() === _target) {
          if (type === 'value') cell[i][j] = value;
          else parent[i][j] = value;
        }
      }
    }
  }

  function update(param) {
    const [r, c, value] = param;
    const target = find([r, c]);
    updateAll(target, value, 'value');
  }

  function replace(param) {
    const [value1, value2] = param;
    for (let i = 0; i < 51; i++) {
      for (let j = 0; j < 51; j++) {
        if (cell[i][j] === value1) {
          cell[i][j] = value2;
        }
      }
    }
  }

  function merge(param) {
    const [r1, c1, r2, c2] = param;
    if (r1 === r2 && c1 === c2) return;

    const value = cell[r1][c1] !== '' ? cell[r1][c1] : cell[r2][c2];
    const parent1 = find([r1, c1]);
    const parent2 = find([r2, c2]);

    if (parent1.toString() !== parent2.toString()) {
      parent[r2][c2] = parent1;
    }

    updateAll(parent2, parent1, 'parent');
    updateAll(parent1, value, 'value');
  }

  function unmerge(param) {
    const [r, c] = param;
    const value = cell[r][c];
    const _target = find([r, c]).toString();

    for (let i = 0; i < 51; i++) {
      for (let j = 0; j < 51; j++) {
        if (parent[i][j].toString() === _target) {
          cell[i][j] = '';
          parent[i][j] = [i, j];
        }
      }
    }

    cell[r][c] = value;
  }

  function print(param) {
    const [r, c] = param;

    return cell[r][c] === '' ? 'EMPTY' : cell[r][c];
  }
}

리팩토링

코드가 지저분해 보였다. 원래 updateAll을 통해서 코드의 중복을 줄여보려고 했었는데, 이중 for문 때문에 여전히 지저분해 보이기는 마찬가지였다.
반복문 내에서의 범위는 같았고 조건과 작업만 살짝씩 달랐기 때문에 callback 함수를 줘서 반복문을 돌리는 iterateAll 함수를 만들었고, toString으로 parent를 비교하는 부분들도 지저분해 보여서 isSameCoords 함수를 만들어 리팩토링했다.
함수의 순서도 살짝 조정했다.

function solution(commands) {
  let answer = [];
  const cell = Array(51)
    .fill()
    .map((_) => Array(51).fill(''));
  const parent = Array(51)
    .fill()
    .map((_, i) =>
      Array(51)
        .fill()
        .map((_, j) => [i, j])
    );

  for (const commandStr of commands) {
    const [command, ...rest] = commandStr.split(' ');

    switch (command) {
      case 'UPDATE':
        if (rest.length === 3) update(rest);
        else replace(rest);
        break;
      case 'MERGE':
        merge(rest);
        break;
      case 'UNMERGE':
        unmerge(rest);
        break;
      case 'PRINT':
        answer = [...answer, print(rest)];
        break;
    }
  }

  return answer;

  function update(param) {
    const [r, c, value] = param;
    const target = find([r, c]);

    iterateAll((i, j) => {
      if (isSameCoords(parent[i][j], target)) cell[i][j] = value;
    });
  }

  function replace(param) {
    const [value1, value2] = param;

    iterateAll((i, j) => {
      if (cell[i][j] === value1) cell[i][j] = value2;
    });
  }

  function merge(param) {
    const [r1, c1, r2, c2] = param;
    if (r1 === r2 && c1 === c2) return;

    const value = cell[r1][c1] !== '' ? cell[r1][c1] : cell[r2][c2];
    const parent1 = find([r1, c1]);
    const parent2 = find([r2, c2]);

    if (!isSameCoords(parent1, parent2)) {
      parent[r2][c2] = parent1;
    }

    iterateAll((i, j) => {
      if (isSameCoords(parent[i][j], parent2)) {
        parent[i][j] = parent1;
      }
      if (isSameCoords(parent[i][j], parent1)) {
        cell[i][j] = value;
      }
    });
  }

  function unmerge(param) {
    const [r, c] = param;
    const value = cell[r][c];
    const target = find([r, c]);

    iterateAll((i, j) => {
      if (isSameCoords(parent[i][j], target)) {
        cell[i][j] = '';
        parent[i][j] = [i, j];
      }
    });

    cell[r][c] = value;
  }

  function print(param) {
    const [r, c] = param;

    return cell[r][c] === '' ? 'EMPTY' : cell[r][c];
  }

  function find(coord) {
    const [r, c] = coord;

    if (isSameCoords([r, c], parent[r][c])) {
      return [Number(r), Number(c)];
    }

    parent[r][c] = find(parent[r][c]);

    return parent[r][c];
  }

  function isSameCoords(coord1, coord2) {
    return coord1.toString() === coord2.toString();
  }

  function iterateAll(callbackFn) {
    for (let i = 0; i < 51; i++) {
      for (let j = 0; j < 51; j++) {
        callbackFn(i, j);
      }
    }
  }
}

0개의 댓글