210407. Today I Learned(TIL) : 객체 코플릿 21번 풀이

syong·2021년 4월 7일
0

TIL

목록 보기
5/32

객체 코플릿의 마지막 끝판왕 문제를 풀어보았다. 몇날 며칠을 테스트 케이스 돌려보고, 디버거를 돌려보고, 식을 갈아엎었다가 다시 썼다가를 여러번 반복했지만 결국 10개의 테스트 중 8개의 테스트 통과가 최선이라 레퍼런스를 보고 공부하기로 했다. 우선 내 코드의 큰 문제점 몇가지를 짚고 넘어가보려고 한다.

<객체 코플릿 21번>
문제: 문자열을 입력받아 가장 많이 반복되는 문자(letter)를 리턴해야 합니다.

입력:
인자 1 str: string 타입의 공백이 있는 문장

출력:
string 타입을 리턴

주의사항:

  • 띄어쓰기는 제외합니다.
  • 가장 많이 반복되는 문자가 다수일 경우, 가장 먼저 해당 횟수에 도달한 문자를 리턴해야 합니다.
  • 빈 문자열을 입력받은 경우, 빈 문자열을 리턴해야 합니다.

우선, 내 최종 코드는 다음과 같다.

function mostFrequentCharacter(str) {
  if(str.length === 0 || str === ' ') {
    return '';
  }

  let obj = {};
  for(let i = 0; i < str.length; i++) {
   if(obj[str[i]] === undefined) {
     obj[str[i]] = 0;
   }
   obj[str[i]]++
  }

   let arr = Object.entries(obj);
   let emp = [];
   let result = 0;
   for(let f = 0; f < arr.length; f++) {
     if(arr[f][0] !== ' ' && arr[f][1] > result) {
       result = arr[f][1];
       emp.push(arr[f][0]);
     }
   }
   return emp[emp.length-1];
 }

테스트 케이스 돌려막기 식으로 코드를 짜다 보니 상당히 코드가 지저분하다. 어찌저찌 빈 문자열을 입력받은 경우를 첫번째 조건문에 추가해서 해당 케이스를 커버했지만 공백 한칸을 입력받은 경우는 결국 커버하지 못했다. 디버거를 돌려봤을 때도 위 코드에서 첫번째 조건문에서 걸려서 빈 문자열을 리턴하는데 성공했었는데 이상하게 테스트 케이스는 통과하지 못했다. 하지만 이 코드는 그것 외에도 전반적으로 손봐야할 곳이 많기 때문에 그 부분은 우선순위를 뒤로 두고 처음부터 다시 보자.

다음은 레퍼런스 코드다.

function mostFrequentCharacter(str) {
  let obj = { mostCount: 0, mostFrequent: '' };
  for (let i = 0; i < str.length; i++) {
    if (str[i] === ' ') {
      continue;
    }

    if (obj[str[i]] === undefined) {
      obj[str[i]] = 0;
    }
    obj[str[i]] += 1;

    if (obj[str[i]] > obj['mostCount']) {
      obj['mostCount'] = obj[str[i]];
      obj['mostFrequent'] = str[i];
    }
  }
  return obj['mostFrequent'];
}

코드 로직 자체가 완전히 뒤틀렸다는 느낌 보다는 디테일에서 큰 차이를 보인다. 순서를 매겨서 차례대로 살펴보면 다음과 같은 차이가 있다.

  1. 내가 쓴 코드는 공백을 객체 안에 불필요하게 집어 넣고 두번째 반복문에서 이를 조건으로 제외시켜주었다. 반면 레퍼런스 코드는 처음 반복문으로 객체를 만들 때 공백을 건너뛰고 키를 생성하도록 'continue' 라는 문법을 사용했다. 나는 반복문에서 아직 'continue'와 'break'가 익숙하지 않아 이 방법을 미처 생각하지 못했다. 공백을 처음부터 제거하고 객체를 만들고 나니 뒷쪽 연산이 상당히 깔끔해졌다.

  2. 내 코드에서는 객체 속성의 값들을 비교하는 과정에서 인덱스로 접근하려는 생각에 객체를 키, 값 쌍의 배열로 만들어주는 Object.entries() 메서드를 사용한 반면, 레퍼런스 코드에서는 객체를 만들 때부터 빈 객체가 아닌 임의의 키(mostCount, mostFrequent)와 그에 맞는 값을 초기값으로 선언한 뒤, 조건에 부합할 시 해당 키들의 값을 재할당해주는 방식으로 구현했다. 최종 작성 코드를 쓰기 전에 여러번의 시행착오들 중, 비교는 객체의 값, 즉 숫자를 비교해야하는데 리턴은 해당 문자열을 리턴해야 하는 문제 때문에 골치가 아팠다. 내가 아는 요령 안에서는 값에 해당하는 키를 새 배열에 차례대로 넣어준 뒤 그 배열의 마지막 요소를 리턴하는 방식으로 돌아 돌아 몇개의 테스트 케이스를 통과했지만, 이렇게 되면 모든 경우의 수를 다 커버할 수 없기 때문에 좋은 코드가 아니게 된다. 이 문제에서는 테스트 케이스가 10개 뿐이지만 만약 이 문제의 테스트 케이스가 몇 십개씩 되었다면 아마 절반은 통과하지 못했을 것이다.

  3. 내 코드는 주의사항 중 '가장 많이 반복되는 문자가 다수일 경우, 가장 먼저 해당 횟수에 도달한 문자를 리턴'의 조건을 충족시키지 못했다. 아무래도 반복문을 따로 두개를 사용하기도 했고 객체를 배열로 변환시켜서 인덱스로 접근을 하는 바람에 '가장 앞에 있는 문자'를 리턴할 뿐, '가장 먼저 도달한 문자'를 리턴할 수는 없었다. 레퍼런스 코드에서는 반복문 하나에 객체를 만드는 동시에 미리 선언해주었던 'mostCount'와 'mostFrequent'의 값들을 재할당 해주기 때문에 '가장 먼저 도달한 문자'를 리턴할 수 있게 되었다.

이번 문제에서는 반복문에서 'continue'문법을 어떻게 사용하는 지 배울 수 있었고, 또한 객체의 키와 값에 접근하는 새로운 방법을 익힐 수 있었다.

0개의 댓글