프로그래머스 152995 : 인사고과 - Javascript

프로그래머스 152995 : 인사고과 - Javascript

2023-02-02
0
views

문제 설명

완호네 회사는 연말마다 1년 간의 인사고과에 따라 인센티브를 지급합니다. 각 사원마다 근무 태도 점수와 동료 평가 점수가 기록되어 있는데 만약 어떤 사원이 다른 임의의 사원보다 두 점수가 모두 낮은 경우가 한 번이라도 있다면 그 사원은 인센티브를 받지 못합니다. 그렇지 않은 사원들에 대해서는 두 점수의 합이 높은 순으로 석차를 내어 석차에 따라 인센티브가 차등 지급됩니다.

이때, 두 점수의 합이 동일한 사원들은 동석차이며, 동석차의 수만큼 다음 석차는 건너 뜁니다. 예를 들어 점수의 합이 가장 큰 사원이 2명이라면 1등이 2명이고 2등 없이 다음 석차는 3등부터입니다.

각 사원의 근무 태도 점수와 동료 평가 점수 목록 scores이 주어졌을 때, 완호의 석차를 return 하도록 solution 함수를 완성해주세요.


제한사항

  • 1 ≤ scores의 길이 ≤ 100,000
  • scores의 각 행은 한 사원의 근무 태도 점수와 동료 평가 점수를 나타내며 [a, b] 형태입니다.
    • scores[0]은 완호의 점수입니다.
    • 0 ≤ a, b ≤ 100,000
  • 완호가 인센티브를 받지 못하는 경우 -1을 return 합니다.

입출력 예

scoresresult
[[2,2],[1,4],[3,2],[3,2],[2,1]]4
  • 5 번째 사원은 3 번째 또는 4 번째 사원보다 근무 태도 점수와 동료 평가 점수가 모두 낮기 때문에 인센티브를 받을 수 없습니다.

  • 2 번째 사원, 3 번째 사원, 4 번째 사원은 두 점수의 합이 5 점으로 최고점이므로 1 등입니다.

  • 1 등이 세 명이므로 2 등과 3 등은 없고 1 번째 사원인 완호는 두 점수의 합이 4 점으로 4 등입니다.


풀이 과정

전체 선수(participant) 중 완주한 선수(completion) 목록에 존재하지 않는 선수 한 명을 찾아내야 합니다.


시도 1

spliceindexOf를 이용해 완주한 선수(completion)를 전체 선수(participant)에서 하나씩 소거한 뒤, 남은 한 선수의 이름을 반환하는 방식으로 풀어봤습니다.

function solution(scores) {
  const len = scores.length;
  for(let i = 1; i < len; ++i){
    if(scores[0][1] < scores[i][0] && scores[0][1] < scores[i][1]){
      return -1;
    }
  }
 
  const totalScores = scores.map([a,b] => a + b);
}

하지만 정확도 테스트는 통과하고 시간 초과로 효율성 테스트는 통과하지 못했습니다. 배열을 수정하는 splice가 느리기 때문에 생긴 문제인 듯 합니다.


시도 2

아래와 같이 해쉬를 이용해 문제를 다시 풀었고, 성공적으로 효율성 테스트까지 통과했습니다.

function solution(participant, completion) {
  // 완주한 선수의 이름의 개수를 저장할 변수 선언
  const counts = {};
 
  // 완주한 선수의 이름의 개수를 `counts` 변수에 담기
  for (name of completion) {
    if (counts[name]) {
      counts[name]++;
    } else {
      counts[name] = 1;
    }
  }
 
  for (name of participant) {
    if (counts[name]) {
      counts[name]--; // 선수 이름이 있으면 개수 1 감소
    } else {
      return name; // 선수 이름이 없거나 0이면 해당 선수의 이름을 반환
    }
  }
}

숏코딩 (CodeGolf)

조금 더 코드를 간결하게 바꾸기 위해 조건식 부분을 수정해 아래와 같이 정리 가능합니다.

function solution(participant, completion) {
  const counts = {};
  for (name of completion) counts[name] = (counts[name] | 0) + 1;
  for (name of participant) if (!counts[name]--) return name;
}

:::help countsname이 없을 수도 있는데 !counts[name]--이 왜 제대로 작동하나요?

  1. counts[name]가 비어있을 경우 접근하면 undefined를 반환합니다.
  2. undefined가 산술 연산자와 만나면 오류 없이 NaN으로 바뀌게 됩니다.
  3. NaN은 논리값 거짓으로 변환되기 때문에, !NaNtrue로 변환됩니다.
  • 따라서 counts[name]이 존재하지 않을 때, !counts[name]--true으로 변환됩니다.

이러한 과정을 통해 매핑된 값이 없는 경우에도 선수의 이름을 반환하는 로직이 정상적으로 작동하게 됩니다.

:::


추가로 counts 객체 생성 라인을 제거하고 completion 배열을 해쉬맵 용도로 재사용합니다.

function solution(participant, completion) {
  for (name of completion) completion[name] = (completion[name] | 0) + 1;
  for (name of participant) if (!completion[name]--) return name;
}

:::help completion는 배열인데 어떻게 키값을 인덱스로 사용 할 수 있나요?

  • 자바스크립트에서는 배열 또한 Array라는 객체이기 때문에 속성(property)을 추가할 수가 있습니다.
  • 따라서 completion[name]이라는 코드가 정상적으로 작동하게 됩니다.

:::


for-ofmap()find() 메소드로 바꾸고 한줄의 코드로 합칩니다.

function solution(participant, completion) {
  completion.map(name => (completion[name] = (completion[name] | 0) + 1));
  return participant.find(name => !completion[name]--, completion);
}
solution = (participant, completion) =>
  participant.find(
    name => !completion[name]--,
    completion.map(name => (completion[name] = (completion[name] | 0) + 1))
  );

마지막으로 가독성을 포기하고 모든 이름을 한글자로 수정하면 아래와 같이 숏코딩 코드가 완성됩니다.

solution = (p, c) =>
  p.find(
    n => !c[n]--,
    c.map(n => (c[n] = (c[n] | 0) + 1))
  );