베스트 앨범

YEONGHUN KO·2021년 12월 18일
0

ALGORITHMS - PRACTICE

목록 보기
41/50
post-thumbnail

1. 베스트 앨범

각 장르마다 곡이 들어가 있다. 이때 각 장르별로 가장 재생이 많이 된 상위 2곡을 선정해서 앨범을 만들려고 한다. 예시를 살펴보자

                     genres	                              plays	              return
["classic", "pop", "classic", "classic", "pop"]	[500, 600, 150, 800, 2500]	[4, 1, 3, 0]

우선 제일 많이 재생된 장르 순으로 곡 2개가 담긴다. pop(3,100회 재생 됨) , classic(1,450회 재생 됨) 이므로 pop에서 가장 많이 재생된 곡 2개가 먼저 담긴다.

이때 재생된 횟수가 높은것이 먼저 담긴다.(4,1)

재생횟수가 같다면 고유번호가 적은것이 먼저 담긴다.

장르내에서 재생된 곡이 1개밖에 없다면 그걸 담는다.

2. 접근 방법

두 가지 solution이 있다. 두개다 기가막힌 solution이다.

2-1. 첫번째 solution

  • pseudo code
    • dic을 만들어서 장르별 총 재생횟수를 저장한다
    • genres를 map으로 순회하면서 genre,count(재생횟수),idx(고유번호),를 obj에 담아서 저장한다
    • 그 결과를 그대로 이어받아서 sort한다.(genre별, 재생횟수별, 고유번호별로 다중 sort를 할 수 있다)
      • 장르가 다르면 dic을 사용해서 genre 별로 sort
      • count가 다르면 count 별로 sort
      • idx가 다르면 idx 별로 sort
    • filter를 통해 상위 랭크 2개만 선정하고 나머진 걸러냄
      • dupDic에 genre별로 갯수를 저장
      • dupDic에 genre갯수가 2이상이면 false로 걸러냄
    • 이제 완성된 obj안에서 map을 통해서 idx만 걸러내면 끝!
function solution1(genres, plays) {
  let dic = {};
  genres.forEach((t, i) => {
    dic[t] = dic[t] ? dic[t] + plays[i] : plays[i];
  });

  let dupDic = {}; // 장르당 곡이 3개넘어가는게 있는지 판별하기 위함.

  return genres
  // 장르, 재생수, 고유번호를 각각 담음. sort할때 쓰려고.
    .map((t, i) => ({ genre: t, count: play[i], index: i }))
    // sort의 다중정렬 가능.
    .sort((a, b) => {
      // 장르가 다르면 위의 dic을 사용하여 가장 인기있는 장르를 토대로 내림차순정렬
      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;
    });
    // 장르 내에서 상위 랭크 2개만 선정하고 나머지는 false를 통해 걸러짐.
            // dupDic 에서 장르의 갯수가 담김.
    .filter((t,i)=>{
      if(dupDic[t.genre] >=2) return false
       dupDic[t.genre] = dupDic[t.genre]?dupDic[t.genre] +1:1
       return true
       // 최종적으로 정렬되고 걸러진 상태에서 고유번호만 순서대로 추출하면 끝!
    }).map(t => t.index)

}

 filter전까지의 상황임

0: {genre: "pop", count: 2500, index: 4}
1: {genre: "pop", count: 600, index: 1}
2: {genre: "classic", count: 800, index: 3}
3: {genre: "classic", count: 150, index: 0}
4: {genre: "classic", count: 150, index: 2}

여기서 마지막에 클래식 obj는 필터링되서 없어짐. 장르당 가장 많이 재생된것 2개만 있어야 하므로.

깔끔 그 자체다...

크... 외국어를 유창하게 하는 사람을 보는것 같은 느낌..섹시하다.

깔끔 세련 편함 그냥 기분좋게 하는 요소는 다들어가 있다고 보면 됨.

요걸 계속 적어보면서 연습하자. sort,filter,map 잘 사용했고, obj도 적절히 잘 사용한 것 같다.

2-2. 두 번째 solution

  • pseudo code
    • songs안에 genre,play,no(고유번호)를 key로 삼고 obj안에 저장해서 자료구조를 만듬
    • 장르와 재생된 횟수를 합산해서 genrePlayCnt에 push함
      • thisGenre안에 songs의 parameter로 현재 song에 있는 genre만 find해서 담아줌
      • thisGenre가 없으면 genre,play를 obj안에 담아서 genrePlayCnt에 push해줌
      • 그게 아니면 thisGenre.play에다가 song의 play를 더함.
    • 그렇게 장르별로 통틀어서 재생횟수가 구해지면 genrePlayCnt를 내림차순 sort함
    • 내림차순된 genrePlayCnt를 forEach를 통해 loop한다.
      • thisGenreSongs 에다가 현재 genre와 같은 genre를 songs에서 추출해서 할당한다
      • play순으로 내림차순(장르별로는 이미 정렬했으므로, 같은 상태이므로 장르를 정렬할 필요는 없다)
      • 그럼 제일 앞에 두개의 고유번호만 answer에 push하면 됨!
function solution2(genre, plays) {
  let answer = [];
  let songs = genres.map((genre, index) => {
    return {
      no: index,
      genre: genre,
      play: plays[index],
    };
  });
  let genrePlayCnt = [];
  // 장르와 재생된 횟수를 합산해서 genrePlayCnt에 push함

  songs.forEach((song) => {
    let thisGenre = genrePlayCnt.find(
      (genrePlay) => genrePlay.genre === song.genre
    );
    if (!thisGenre) {
      genrePlayCnt.push({
        genre: song.genre,
        play: song.play,
      });
    } else thisGenre.play += song.play;
  });
  // 장르 통틀어서 재생된 횟수를 내림차순으로 정렬

  genrePlayCnt.sort((a, b) => b.play - a.play);

  genrePlayCnt.forEach((genrePlay) => {
    // genrePlayCnt에서 정렬한 대로 하나씩 loop함
    // pop이 먼저이므로 pop에 해당하는 song만 뽑아내서 thisGenreSongs에 저장
    let thisGenreSongs = songs.filter((song) => song.genre === genrePlay.genre);
  });
  // 플레이 순으로 내림차순. 자동으로 고유번호랑 같이 정렬됨
  thisGenreSongs.sort((a, b) => b.play - a.play);
  // 제일 많이 플레이된곡의 고유번호를 answer에 push

  answer.push(thisGenreSongs[0].no);
  // 그 다음 2번째 곡이 있으면 2번째 곡의 고유번호를 push함
  if (thisGenreSongs.length > 1) {
    answer.push(thisGenreSongs[1].no);
  }

  return answer;
}

첫 번째 만큼 깔끔한건 아니지만 나름 로직을 잘 정리해서 구현했다.

obj를 이용해서 정보를 잘 정리하고 필요할때마다 뽑아쓰는 기술이 돋보임.

그냥 두개다 너무 친근하고 깔끔한 느낌이다.

진짜 오늘 또 느낀다. 매일 느낀다. 너무 감사하고 신기하고 뿌듯하다...눈물나올것 같다.

profile
'과연 이게 최선일까?' 끊임없이 생각하기

0개의 댓글