구현기/알고리즘 문제풀이

[프로그래머스 코딩테스트 연습] 베스트 앨범 JS

Sadie Kim 2023. 6. 12. 18:25

https://school.programmers.co.kr/learn/courses/30/lessons/42579

나의 풀이

슈도 코드

  1. 먼저 장르별로 담고 있는 음악과 총 재생수를 저장하기 위한 genreDic 딕셔너리를 정의한다.
    genreDic = {
     classic : {
         music : [각 장르에 담긴 고유번호 배열],
         count : 각 장르의 재생수 합
     },
     pop : {
     ...
     },
     ...
    }
  2. genres를 반복문으로 돌리면서 genreDic의 각 장르별 music 배열 데이터를 채운다.
  3. genreDic 객체의 각 장르별 music 배열을 반복문으로 돌리면서 plays 배열을 참고해 count 합계를 계산하고, genreDic의 count를 채운다.
  4. genreDic의 장르들을 count들로 sort해서 새 배열 genreArr을 정의한다.
  5. genreArr을 반복으로 돌리면서 genreDic에서 해당하는 장르의 music 배열을 가져오고, 해당 배열을 count와 id별로 sort한 뒤 두 번째 원소까지만 구해서 answer에 push한다.

풀이 코드

function solution(genres, plays) {
  var answer = [];
  const genreDic = {};
  //genreDic의 각 장르별 music을 채운다.
  genres.map((genre, idx) => {
    if (!genreDic[genre]) {
      genreDic[genre] = {
        music: [idx],
        count: 0,
      };
    } else {
      genreDic[genre].music.push(idx);
    }
  });
  //genreDic의 각 장르별 count를 채운다.
  Object.entries(genreDic).map(([key, { music }]) => {
    const count = music.reduce((acc, cur) => {
      return acc + plays[cur];
    }, 0);
    genreDic[key].count = count;
  });
  //장르별 count로 정렬한 genreArr을 생성한다.
  const genreArr = Object.keys(genreDic).sort(
    (a, b) => +genreDic[b].count - +genreDic[a].count
  );
  //genreArr을 돌리면서 해당하는 장르의 곡들을 정렬, 2개씩 slice하고 answer에 push한다.
  genreArr.map((genre) => {
    const songs = genreDic[genre].music;
    const arr = songs.sort((a, b) => {
      return +plays[b] - +plays[a] || +a - +b;
    });
    answer.push(...arr.slice(0, 2));
  });
  return answer;
}

다른 사람의 풀이

내 풀이는 뭔가 많이 추려냈는데도 조금 지저분했는데, 제출한 뒤 다른 사람의 풀이를 보니 깔끔한 코드가 있어서 올려 본다.

슈도 코드

  1. 장르별 재생수 합을 갖게 될 dic 딕셔너리를 정의한다. 형식은 다음과 같다.
    dic = {
     classic : 각 장르의 재생수 합,
     pop : 각 장르의 재생수 합,
     ...
    }
  2. genres를 반복문으로 돌리며 dic을 채운다.
  3. answer에 제출할 장르별 곡 수 정보를 담을 dupDic 딕셔너리를 정의한다.
  4. genres를 반복문으로 돌리며 곡별 정보를 담고 있는 새 배열을 정의한다. 각 배열이 담고 있는 객체 원소의 형식은 다음과 같다.
     {
         genre : 곡의 장르,
         count : 곡의 재생수,
         index : 곡의 고유번호(인덱스)
     }
  5. 4의 배열을 sort한다. 첫 번째로는 dic을 참고한 장르별 count 합으로 sort하고, 같은 장르일 시 각 곡의 count로 sort한다. 곡 count가 같을 시 고유번호로 sort한다.
  6. 5의 배열에서 각 장르별로 2곡씩 추린다. 추리는 과정에서 dupDic을 이용한다.
  7. 6에서 인덱스만 뽑아서 answer로 리턴한다.

풀이 코드

function solution(genres, plays) {
    var dic = {};
    genres.forEach((t,i)=> {
        dic[t] = dic[t] ?  dic[t] + plays[i] :plays[i];        
    });

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

참고한 코드는... 우선 메소드 체이닝을 이용해서 간결하게 만든 게 깔끔해서 좋았고...
조금 복잡한 내 딕셔너리와 다르게 각 장르별 재생수 합만 넣어서 dic을 만들어 둔 후 나머지는 genres, plays에서 각자 가져와서 휘릭휘릭 계산한 점도 간단해서 좋았다.
또 filter를 저렇게 저런 식으로... index값을 활용하지 않고도 선착 n개만 받는 형식을 구현할 수 있다니 처음 보는 패턴이어서 새로웠다. 나중에 활용할 일이 있을 것 같다.