본문 바로가기

환영합니다. 이 블로그 번째 방문자입니다.
Python/문제풀이

[ ➕ 오답노트] 프로그래머스 해시 - 베스트앨범

아... 이 문제... 실패했다...ㅎ

미련 없이 해답을 보기로..!

 

👇🏻 문제

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

 

코딩테스트 연습 - 베스트앨범

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

programmers.co.kr


 

 

정답 코드

 

def solution(genres, plays):
    answer = []
    d = {e:[] for e in set(genres)}
    for e in zip(genres, plays, range(len(plays))):
        d[e[0]].append([e[1] , e[2]])
    genreSort =sorted(list(d.keys()), key= lambda x: sum(map(lambda y: y[0], d[x])), reverse = True)
    for g in genreSort:
        temp = [e[1] for e in sorted(d[g],key= lambda x: (x[0], -x[1]), reverse = True)]
        answer += temp[:min(len(temp),2)]
    return answer

 

이런 코드는 어떻게 짜는 겁니까 정말. 내 코드는 47줄인데...

한 줄 한 줄 살펴보자...

 

# 입력 값
genres=["classic", "pop", "classic", "classic", "pop"]	
plays=[500,600,150,800,2500]

 

 

 

 

1. 해시(딕셔너리) 선언하기  d = {'pop': [], 'classic': []} 

d = {e:[] for e in set(genres)}

# 내 코드
d = {}
for i in range(len(genres)):
   if genres[i] not in play_list: 
      play_list[genres[i]]=[]

 

[배울점 1] 위와 아래 놀랍게도 같은 코드...다음부터는 위의 방식으로 쓰도록 하자...(이 바보 멍충아...)

 

 

 

 

 

 

2. 딕셔너리에 값 넣기  d = {'pop': [[600, 1], [2500, 4]], 'classic': [[500, 0], [150, 2], [800, 3]]} 

 

for e in zip(genres, plays, range(len(plays))):
        d[e[0]].append([e[1] , e[2]])

 

genres, plays, len(plays)의 길이가 같으니깐, zip 함수를 이용했다.

아마 나였으면 또 아래와 같이 썼겠지...

 

# 나였으면 - ㄴㅇㄱ
for genre, play, id in zip(genres, plays, range(len(plays))):
        d[genre].append([play , id])

 

[배울점 2] 변수명 보다는 배열로  쓰는게 더 깔끔하군...물론 내 것이 가독성은 좋지만 (미련)

 

 

 

 

 

3. 더 많이 재생된 장르 찾기   genreSort = ['pop', 'classic'] 

 

genreSort =sorted(list(d.keys()), key= lambda x: sum(map(lambda y: y[0], d[x])), reverse = True)

 

이 코드는 배울 수 없다. 과연 내 머리로..?

일단, d의 key들을 배열로 받아 내림차순으로(큰 것 부터) 정렬한다. 문제는 정렬할 키를 어떻게 뽑느냐 이다.

 

 key= lambda x: sum(map(lambda y: y[0], d[x]))

 

map에 사용할 함수가 일회용이고 길이가 짧기 때문에 임시함수 lambda를 사용하는 것이 효율적이다.

난 lambda에 익숙하지 않아서 저 함수를 일일히 다 작성해보았다.

 

def play_count(y):
	return y[0]

def sum_play_count(x):
	return sum(map(play_count, d[x]))

key = sum_play_count

 

d[x] 는 d.keys()의 집합이다.

map 함수에서 d[x] 에 있는 값들 중 첫번째 요소(y[0] === d[x][0], play_count)를 순회하며 더해준 값(sum)이 정렬 key가 된다.

참고로, 나는 반복문으로 일일히 더했다.ㅋ. 

 

 d = {'pop': [[600, 1], [2500, 4]], 'classic': [[500, 0], [150, 2], [800, 3]]} 

 

예시) x = 'pop'일 경우

 

d['pop'] = [[600,1],[2500,4]]

y = [600,1] , [2500,4]

y[0] = 600, 2500

 

key 값

'pop' : 600+2500

'classic : 500+150+800

 

 

 

 

 

 

4. 내림차순으로 정렬된 genreSort를 순회하며 가장 많이 재생된 2곡을 answer에 추가해준다. 

만약, 재생 수가 같다면 아이디가 더 먼저인 것을 추가해준다.  answer = [4, 1, 3, 0] 

 

for g in genreSort:
        temp = [e[1] for e in sorted(d[g],key= lambda x: (x[0], -x[1]), reverse = True)]
        answer += temp[:min(len(temp),2)]

 

여기서 또 나와버린 lambda...

 

temp = [e[1] for e in sorted(d[g],key= lambda x: (x[0], -x[1]), reverse = True)]

 

이것도 정말 천재같은 코드 🧞‍♂️ 미쳤다 정말 너무 멋있어

나는 이것을 이해하기 위해 또 엉켜버린 실타래를 풀렀다. (예시는 'pop'으로)

 

new_array = sorted(d['pop'], key=lambda x:(x[0],x[-1]), reverse=True)
print(new_array)

# 출력 [[2500, 4], [600, 1]]

for e in new_array:
	print(e[1])
    
# 출력 4
# 출력 1

 

이게 뭐냐면, d['pop']을 조건에 맞게 정렬해 주는 것이다. 조건은 2가지다.

1. 많이 재생된 노래 : x[0] (첫번째 정렬 키)

2. 만약 재생 수가 같다면 번호가 빠른 것 : x[-1] (두번째 정렬 키)

 

이 두번째 정렬 키는 예를 들어

[[500, 8], [500, 4], [500, 2]] 이렇게 있을 경우, x[0] 이 500으로 같고 번호가 빠른게 먼저 출력되어야 하기 때문에 

[[500, 2], [500, 4], [500, 8]] 이렇게 정렬해야만 한다.

 

하지만, 우리는 x[0] 을 기준으로 이미 내림차순을 하고 있기 때문에 x[1] 을 기준으로 정렬하면

[[500, 8], [500, 4], [500, 2]] 이렇게 된다.

따라서 이를 해결하기 위한 방법이 x[1]이 아니라 -x[1](음수)으로 내림차순하면 x[1]의 오름차순과 같게 된다. 

 

배열을 변형시키지 않는 sorted()를 사용하고 있기에 정렬키를 음수로 생각해 정렬하면 짠 이렇게 된다.

 

이렇게 정렬한 배열의 1번째 원소 [[2500, 4], [600, 1]] 의 배열이 temp가 된다.  temp = [4,1] 

 

for g in genreSort:
        temp = [e[1] for e in sorted(d[g],key= lambda x: (x[0], -x[1]), reverse = True)]
        
        # temp = [4,1]
        answer += temp[:min(len(temp),2)]

 

여기에 문제에서 요구하는 또 하나의 조건을 충족시켜 주어야 한다.

 

장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다. (그렇지 않으면, 2개 선택)

 

이것을 충족시키기 위해 len(temp)와 2 중 작은 값을 찾아 곡이 하나이면 temp[:1],

두 곡 이상이면 temp[:2] 를 answer에 더해주면 된다.

 


하하...이 문제 이해하는데 2시간 걸렸다. 1주일 후에 다시 풀어보겠다.