[Programmers] 베스트 엘범

김유진·2023년 6월 7일
0

PS

목록 보기
34/36
post-thumbnail

문제 링크

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

문제 유형

해시

🌈 문제

스트리밍 사이트에서 장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시하려 합니다. 노래는 고유 번호로 구분하며, 노래를 수록하는 기준은 다음과 같습니다.

  • 속한 노래가 많이 재생된 장르를 먼저 수록합니다.
  • 장르 내에서 많이 재생된 노래를 먼저 수록합니다.
  • 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.
    노래의 장르를 나타내는 문자열 배열 genres와 노래별 재생 횟수를 나타내는 정수 배열 plays가 주어질 때, 베스트 앨범에 들어갈 노래의 고유 번호를 순서대로 return 하도록 solution 함수를 완성하세요.

제한사항

  • genres[i]는 고유번호가 i인 노래의 장르입니다.
  • plays[i]는 고유번호가 i인 노래가 재생된 횟수입니다.
  • genres와 plays의 길이는 같으며, 이는 1 이상 10,000 이하입니다.
  • 장르 종류는 100개 미만입니다.
  • 장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.
  • 모든 장르는 재생된 횟수가 다릅니다.

입력

genres : ["classic", "pop", "classic", "classic", "pop"]
plays: [500, 600, 150, 800, 2500]

출력

[4, 1, 3, 0]

💡 아이디어

이 문제에 대해 이야기하자면 출석부 같은 친구이다. 탐색을 많이 해야 하기 때문에 탐색에 대한 시간을 가장 짧게 가져가는 알고리즘으로 풀어야 할 것이다. 
그렇기 때문에 해시의 개념을 이용해서 풀이해야 한다.
내가 사용하는 파이썬 같은 경우 해시의 자료구조를 dictionary를 이용하면 구현할 수 있으나 이 문제는 dic을 이용하면 배열이 너무 길고 복잡해져서 인덱스를 key로 지정하여 여러가지 배열로 겹쳐 있는 형식으로 풀이하고자 하였다.

구현하는 데 있어 코드가 조금 길고 복잡할 뿐이지 엄청 어려운 문제는 아니다.

👨‍💻 코드 작성

def solution(genres, plays):
	#장르를 집합으로 정리한다.
    genrelst = list(set(genres))
    maxplay = [[0, i] for i in range(len(genrelst))]
    albumlst = [[] for _ in range(len(genrelst))]
    for i in range(len(genres)):
        indx = genrelst.index(genres[i])
        albumlst[indx].append([plays[i], i])
        maxplay[indx][0] = maxplay[indx][0] + plays[i]
 # 재생횟수가 높은 음악대로 내림차순 정렬 (각각의 장르에 대하여)
    for i in range(len(genrelst)):
        albumlst[i].sort(key=lambda x: x[0], reverse = True)
    answer = []
# 총 재생횟수가 가장 높은 장르들 순으로 내림차순 정렬 
    maxplay.sort(key=lambda x : x[0], reverse=True)
    for i in range(len(genrelst)):
        genre = maxplay[i][1]
        newlist = albumlst[genre]
        #만약 두개 이상이라면 두개의 리스트를 뽑아 넣기
        if len(newlist) >= 2:
            answer.append(newlist[0][1])
            answer.append(newlist[1][1])
        else:
            answer.append(newlist[0][1])
    return answer

이렇게 파이썬으로 노가다 풀이해도 좋지만, 이왕이면 자바스크립트로 코테를 두개 다 동시에 준비하는 입장에서 자바스크립트 풀이는 어떻게 되는지 들어봐야 할 것이다.

자바스크립트 같은 경우 해시에 대한 내용을 Map 자료형이나 Set 자료형, 단순한 배열, 아님 리터럴 객체 형식으로 정의해도 좋다. 뭐 어쨌든 객체형이면 다 좋다는 소리.
이 문제는 다양한 고차함수를 이용하여 풀이해도 좋은데, 어떻게 풀이하면 좋을지 큰 흐름만 잡아보자.

1. Map 객체를 적극 이용하자.

기존에 존재하는 Object 객체를 사용해도 되지만, ES6부터 새로 도입된 Map 객체 같은 경우에는 문자열 뿐만 아니라 숫자도 key값으로 들어갈 수 있다. Map 객체는 key-value값을 저장하여 key로 자료형을 구분한다.
대신, 세팅할 때는 아래와 같이 코드를 작성해야 한다.

let mapTest = new Map();
mapTest.set(1, 'hello');
mapTest.get(1); //'hello';

또한 이러한 Map 객체는 entries라는 키워드를 통하여 Map 객체에 등록한 키, 값 쌍에 대하여 Iterator 객체를 반환할 수 있다. 이 문제에서는 spread문법을 통하여 이터레이터에 접근해야 할 일이 많기 때문에 리터럴 Oject를 그대로 사용하기보다는 Map 객체를 이용하여 문제를 풀이하는 것이 좋아 보인다.

2. 묶자.

이번 문제의 키워드는 바로 "묶기"라고 할 수 있다. 장르와 그에 대한 플레이 횟수를 저장할 수 있는 자료구조를 만들어서 컨트롤하도록 해보자.

const newlst = genres.map((genre, index) => [genre, plays[index]]);

위와 같이 map함수를 이용하여 새로운 배열로 저장하였더니 아래와 같은 새로운 배열이 반환된다.

이제 여기서 끝난다고 생각하면 안된다. 한번 더 묶어야 한다. 같은 장르는 장르끼리 묶고, 그에 대한 인덱스와 플레이 횟수까지 완벽하게 묶어봐야 한다.

genreMap.get(genre) || { total : 0, songs : [] }
genreMap.set(genre, { total : ..., songs: ...})

genre를 불러와서 data에 저장해야 하나, 없을 때는 undefined가 반환되기 때문에 초기값을 미리 세팅해준다. 이후, 불러온 장르에 대해서 총 재생 수인 total과 노래에 대한 정보인 songs를 객체 형식으로 저장해준다. 기존의 정보들도 가져와야 하므로 스프레드 문법을 이용한다.

이후, songs를 내림차순으로 정렬해준 이후 2개씩 자르기 위해서 slice를 이용하면 된다.

3. 풀어주자.

...genreMap.entries()

이제 Map 객체를 iterator 형식으로 반환해주는 entries 함수를 이용하자. 그럼 아래와 같이 순환 가능한 자료형 형식으로 Map이 반환된다. 이제 배열에 담은 이후, total 을 내림차순으로 정렬하고, songs 배열 객체를 정리하여 보기 좋게 정답을 내보자.

이제 각 배열에 대한 인덱스 1번에 대해서 집중하면 된다. total에 대하여 내림차순으로 정리해준 이후에 songs에 대한 배열을 flatMap 을 이용하여 풀어주자. 그리고 index만을 가지는 새로운 배열을 반환하면 정답을 얻을 수 있다.

이렇게 자바스크립트에서 아직은 잘 알지 못하는 고차함수들에 대하여 문제 풀이를 하면서 새롭게 알 수 있었다. 각 메소드에 대해서 사용법을 게으르게 학습하면 안될 것 같다.!! 파이팅!!

1개의 댓글

comment-user-thumbnail
2023년 6월 7일

이전에 저도 Python으로 유진님과 비슷한 방식으로 풀었는데 오랜만에 JS 고차함수를 써서 다시 풀어보니 느끼는게 참 많은 것 같아요. 글 잘 봤습니다 :)

답글 달기