본문 바로가기
휴게소

부스트캠프 웹﹒모바일 6기 자가진단 후기

by Vintz 2021. 6. 7.
반응형

특정 숫자의 중복 횟수를 계산하는 배열 함수

부스트캠프 웹﹒모바일 6기 자가 진단을 봤다. 자가 진단은 부스트캠프에 지원하기 전 본인의 기초 지식을 점검하고, 내가 과정에 참여 할만한 사람인지 스스로 판단해보는 것이다. 그 중 마지막 문제를 푸는데 어려움을 겪었다. 쉬워 보였는데 막상 풀어보니 굉장히 어려웠다..결국 구글링을 통해 풀긴 했지만 다시 한번 자료구조와 컴퓨팅적 사고의 중요성에 대해 깨닫게 되었다.

문제는 다음과 같았다. 부스트캠프 웹﹒모바일 자가진단

함수 구현

  • 자연수가 들어있는 배열 arr가 매개변수로 주어집니다. 배열 arr안의 숫자들 중에서 앞에 있는 숫자들부터 뒤에 중복되어 나타나는 숫자들 중복 횟수를 계산해서 배열로 return 하도록 solution 함수를 완성해주세요. 만약 중복되는 숫자가 없다면 배열에 -1을 채워서 return 하세요.

입출력 예

arr result
[1, 2, 3, 3, 3, 3, 4, 4] [4, 2]
[3, 2, 4, 4, 2, 5, 2, 5, 5] [3, 2, 3]
[3, 5, 7, 9, 1] [-1]
  • #1 3은 4번, 4는 2번씩 나타나므로 [4, 2]를 반환합니다.
  • #2 2가 3번, 4가 2번, 5가 3번씩 나타나므로 [3, 2, 3]을 반환합니다.
  • #3 중복해서 나타나는 숫자는 없으므로 [-1]을 반환합니다.

코드

const arr1 = [1, 2, 3, 3, 3, 3, 4, 4];
const arr2 = [3, 2, 4, 4, 2, 5, 2, 5, 5];
const arr3 = [3, 5, 7, 9, 1];

function solution(arr) {
  const map = arr.reduce((prev, curr) => {
    prev.set(curr, (prev.get(curr) || 0) + 1);
    return prev;
  }, new Map());

  result = [];
  for (let [key, val] of map) {
    if (val > 1) result.push(val);
  }
  if (result.length === 0) {
    return [-1];
  }
  return result;
}

console.log(solution(arr2));
// [3, 2, 3]

이게 되네..

많은 시행착오를 겪고 결국 풀었다. 각 숫자의 반복 횟수를 가져오는 것에 대해 많이 난감했다. 중복 횟수나 중복을 제거하는 것이 아닌 각 숫자의 반복 횟수다.

어쨋든 객체형식으로 반환하는 것까지는 됐었다. arr1을 예로 들면 {1: 1, 2: 1, 3: 4, 4: 2}을 반환했다. 여기서 또 한번 막혔고 결국 맵을 이용해 풀게 되었다.

그래..이해는 했다 이해는 했지만 결국은 내가 풀었다는 느낌이 스스로 들지 않았고 찝찝한 기분이 들었다. 그대로 따라친건 아니지만 저 코드를 남에게 설명할 수 있을까? 그래서 관련 자료구조를 공부하기로 했다.

Map!!

new Map()을 이용한 자료구조는 처음 사용해보았다. 알아보니 객체와 배열 이 두 자료구조만으론 부족해서 맵이 등장하게 되었고 ES6에 도입된 모던한 자료구조이다.

 

객체와 배열은 다음과 같은 특징을 갖고 있다.

  • 객체 : 키가 있는 컬렉션을 저장함
  • 배열 : 순서가 있는 컬렉션을 저장함

 

여기서 맵은 키가 있는 데이터를 저장한다는 점에서 객체와 유사하지만 맵은 키에 다양한 자료형을 허용하고 삽입 순서를 기억한다.

 

  • new Map() : 맵을 만든다.
  • map.set(key, value) : key를 이용해 value를 저장한다.
  • map.get(key) : key에 해당하는 값을 반환. key가 존재하지 않을 경우 undefined를 반환한다.
  • map을 사용할땐 전용 메서드 set, get 등의 사용을 요구한다. (map[key] = 1 ❌)
const map = new Map();

map.set('1', 'string key');
map.set(1, 'number key');
map.set(true, 'boolean key');

// 객체의 키는 반드시 String or Symbol이어야 한다.
// 맵은 키의 타입을 그대로 유지한다.
console.log(map.get(1)); // number key
console.log(map.get('1')); // string key
console.log(map.get(true)); // boolean key

맵의 요소에 반복 작업하기

바로 이 특징 덕분에 문제를 풀 수 있었다. 맵 자료구조는 배열처럼 반복 작업이 가능하다. `for..of`와 `forEach`를 지원한다! 객체와 배열의 특징을 둘 다 갖고 있기때문에 특정 숫자의 중복 횟수를 풀 수 있었다.

 

result = [];
// {1 => 1, 2 => 1, 3 => 4, 4 => 2}
for (let [key, val] of map) {
  // 값이 1이상인 것만 빈 배열에 push
  if (val > 1) result.push(val);
}

 

6월 7일(월) 해설과 풀이 추가 💡

오늘 부스트캠프 블로그에 자가진단 해설과 풀이 글이  올라왔다. 역시나 풀이에는 배열, 객체와 다른 Map 타입이 쓰였다. 

자바스크립트 풀이 방법 1(왼쪽)과 2(오른쪽) 이미지 출처 - https://blog.naver.com/boostcamp_official/222388429782

하지만 풀이방법은 달랐다. 답을 향한 목적은 같지만 다양한 코드 스타일과 풀이를 보면

 

'아, 이 사람은 이런 식으로 풀었구나. 대단하다.'

'이렇게도 풀이가 가능하네?'

'와..천재 아니야?'  

 

이런 생각이 들면서 보는 재미가 있다.

풀이를 보면서 두 가지를 배우게 되었다. 나는 알고리즘을 풀 때 '알고리즘 하나당 함수 하나만'을 고집했다.

나는 1개의 알고리즘이 1개의 함수라고 생각했던 걸까?

알고리즘 크기가 크거나 어려울 때는 함수를 작은 단위로 나눠보자.

나는 내 풀이에서

const map = arr.reduce((prev, curr) => {
    prev.set(curr, (prev.get(curr) || 0) + 1);
    return prev;
  }, new Map());

구글링 해서 답을 찾았지만 나는 이 코드가

 

'어렵다..동작 순서가 어떻게 되지? console.log()...'

'reduce() 메서드는 배열에서 사용했었는데 용도에 맞는 메서드 사용일까?'

 

라는 생각을 했는데 풀이방법2에선 깔끔하고 쿨한 것 같았다.

이미지 출처 - https://blog.naver.com/boostcamp_official/222388429782

Map.has() 메서드는 주어진 키를 가진 요소가 Map에 존재하는 지를 boolean 타입으로 반환한다. 

키에 대한 값을 보관하고, 키값에 대한 중복을 확인해서 중복 된다면 중복 횟수를 +1해서 저장한다.

위 코드가 나에겐 더 이해하기 편하고 Map 타입에 관련된 메서드를 쓰는게 더 좋아보였다.

반응형