티스토리 뷰

반응형

 

프로그래머스 - 베스트앨범

 

지난 포스트에서는 여러 조건 별로 변수를 나누어서 풀었다면, 이번에는 프로그래머스에서 가장 좋아요를 많이 받은 풀이법에 대해 설명하고자 한다. 너무 깔끔하고, 배열 메서드중에서 몰랐던 기능도 알게되어 여러모로 유익했고 닮고싶은 풀이다.

 

 

 


 

 

이번 풀이 역시 주어진 테스트케이스에는

조건3. 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.

를 확인할 수 없으므로 테스트케이스에 값을 하나 더 추가했다. 따라서 원하는 출력값은 [4, 1, 3, 5] 가 되겠다. 

const genres = ["classic", "pop", "classic", "classic", "pop", "classic"];
const plays = [500, 600, 150, 800, 2500, 800];

output = [ 4, 1, 3, 5 ]

 


 

답안

 

기존 포스팅방식과 반대로 전체 코드부터 보자. 참고로 실제 코드를 그대로 가져온 것은아니다.

변수명이 헷갈려서 좀 더 구분하기 쉽게 바꾸었고, 초기 배열을 객체화하는 부분에서 forEach를 쓰셨는데 나는 reduce를 익히고 싶어 reduce() 메소드를 사용하였다. 

function solution(genres, plays) {
  let obj = genres.reduce(
    (obj, track, i) => (
      (obj[track] = obj[track] ? obj[track] + plays[i] : plays[i]), obj
    ),
    {}
  );

  let dubObj = {};

  return genres
    .map((track, i) => ({ genre: track, count: plays[i], index: i }))
    .sort((a, b) => {
      if (a.genre !== b.genre) return obj[b.genre] - obj[a.genre];
      if (a.count !== b.count) return b.count - a.count;
      return a.index - b.index;
    })
    .filter((song) => {
      if (dubObj[song.genre] >= 2) return false;
      dubObj[song.genre] = dubObj[song.genre] ? dubObj[song.genre] + 1 : 1;
      return true;
    })
    .map((song) => song.index);
}

 

 

 


 

답안 풀이

 

 1. reduce() 메서드로 객체화 

지난 포스트에는 객체화를 하면서 모든 분류를 마쳤다. 하지만 이 풀이에서는 굳이 그렇게 나눌 필요가 없었다.

obj라는 변수에 장르정도만 classic / pop 으로 객체화를 해주고 나머지는 return할 때 (map(), sort(), filter())를 이용하여 결과값을 추출한다. 

 

 

원하는 output =

obj = { classic: 2250, pop: 3100 }

 

code : 

  let obj = genres.reduce(
    (obj, track, i) => (
      (obj[track] = obj[track] ? obj[track] + plays[i] : plays[i]), obj
    ),
    {}
  );

 

2. map() 메서드로 genres에  key, value값으로 분류

genres를 분류하는 작업이다. genres는 genre, plays배열에 있는 것은  count, 노래의 고유번호를 Index라는 key값을 부여하고 그에 맞는 value를 선언해 map()으로 각각 분류를 해주었다. 

 

 

원하는 output 

genres = [
  { genre: 'classic', count: 500, index: 0 },
  { genre: 'pop', count: 600, index: 1 },
  { genre: 'classic', count: 150, index: 2 },
  { genre: 'classic', count: 800, index: 3 },
  { genre: 'pop', count: 2500, index: 4 },
  { genre: 'classic', count: 800, index: 5 }
]

code : 

  return genres
    .map((track, i) => ({ genre: track, count: plays[i], index: i }))

 

 

 

3. sort() 메서드로 genre, count, index를 분류

풀이를 보고 제일 인상깊었던 부분이다. sort()로 다중분류를 할 수 있다는 점을 알았다. 

그리고 코드를 보니 순서대로 우선순위가 적용이 되는 듯했다. 

그래서 장르가 먼저 분류되고, 그 안에 count가 분류, 마지막으로 index가 분류됨으로서 주어진 조건을 모두 만족하게 된다. 

 

 

원하는 output : playCount 값이 큰 순서대로 나열된다. 

sorted = [
  { genre: 'pop', count: 2500, index: 4 },
  { genre: 'pop', count: 600, index: 1 },
  { genre: 'classic', count: 800, index: 3 },
  { genre: 'classic', count: 800, index: 5 },
  { genre: 'classic', count: 500, index: 0 },
  { genre: 'classic', count: 150, index: 2 }
]

code  :

  return genres
    .map((track, i) => ({
      genre: track,
      count: plays[i],
      index: i,
    }))
    .sort((a, b) => {
      if (a.genre !== b.genre) return obj[b.genre] - obj[a.genre];
      if (a.count !== b.count) return b.count - a.count;
      return a.index - b.index;
    });

 

 

 

4. filter() 메서드로 각 장르당 상위 2개만 필터링

 

filter() 메서드는 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복호출한다. 그리고 콜백함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환한다. 

 

filter할 때 한 장르 안에서 3개 이상의 track이 나오면 상위 2개를 제외한 그 뒤의 track를 false함으로 서 dubObj이라는 객체 안에 상위 2개 요소만 가져온다. 그리고 그 true만 리턴하는 filter()메소드의 성질을 이용해 genres 역시 상위 2개의 요소들만 필터링되게 된다.

 

 

원하는 output : 

 dubObj = { pop: 2, classic: 2 } 
 
 // filter()로 인해 dubObj이 true인 요소들만 구성된 새로운 배열을 반환한다.
 genres =  
  { genre: 'pop', count: 2500, index: 4 },
  { genre: 'pop', count: 600, index: 1 },
  { genre: 'classic', count: 800, index: 3 },
  { genre: 'classic', count: 800, index: 5 }

 

 

code :

let dubObj = {};
return genres
    .map((track, i) => ({
      genre: track,
      count: plays[i],
      index: i,
    }))
    .sort((a, b) => {
      if (a.genre !== b.genre) return obj[b.genre] - obj[a.genre];
      if (a.count !== b.count) return b.count - a.count;
      return a.index - b.index;
    })
    .filter((song) => {
      if (dubObj[song.genre] >= 2) return false;
      dubObj[song.genre] = dubObj[song.genre] ? dubObj[song.genre] + 1 : 1;
      console.log(dubObj);
      return true;
    });

 

 

 

4. map()으로 index값 추출

 

마지막으로는 3번의 output에서 index값만 추출하면 된다. 이는 map() 으로 간단하게 구현할 수 있다. 

 

원하는 output :  

answer = [ 4, 1, 3, 5 ]

 

code :

  let dubObj = {};

  return genres
    .map((track, i) => ({
      genre: track,
      count: plays[i],
      index: i,
    }))
    .sort((a, b) => {
      if (a.genre !== b.genre) return obj[b.genre] - obj[a.genre];
      if (a.count !== b.count) return b.count - a.count;
      return a.index - b.index;
    })
    .filter((song) => {
      if (dubObj[song.genre] >= 2) return false;
      dubObj[song.genre] = dubObj[song.genre] ? dubObj[song.genre] + 1 : 1;
      console.log("ㅇ", dubObj);
      return true;
    })
    .map((song) => song.index);

 


 

완성코드

function solution(genres, plays) {
  let obj = genres.reduce(
    (obj, track, i) => (
      (obj[track] = obj[track] ? obj[track] + plays[i] : plays[i]), obj
    ),
    {}
  );

  let dubObj = {};

  return genres
    .map((track, i) => ({ genre: track, count: plays[i], index: i }))
    .sort((a, b) => {
      if (a.genre !== b.genre) return obj[b.genre] - obj[a.genre];
      if (a.count !== b.count) return b.count - a.count;
      return a.index - b.index;
    })
    .filter((song) => {
      if (dubObj[song.genre] >= 2) return false;
      dubObj[song.genre] = dubObj[song.genre] ? dubObj[song.genre] + 1 : 1;
      return true;
    })
    .map((song) => song.index);
}

 


 

Comment

  • sort()로 다중 정렬이 가능하다.
  • filter()의 특정 조건의 반환값이 true인 요소만 리턴하는 특성을 이용할 수 있다. 
  • reduce()로 객체화할 수 있다. 

return에 하나씩 쌓여가는 배열메서드를 보면 볼수록 소름이돋는다.. 프로그래머스는 저렇게 추천순으로 풀이를 볼 수 있어 좋은 것 같다. 

 

 


변수로 조건을 나눈 다른 풀이법 => 바로가기

 

 

 

반응형