프로그래밍/코딩 테스트, 더 이상 미룰 수 없다

[코딩 테스트, 더 이상 미룰 수 없다] 프로그래머스lv3 베스트앨범 - 시험 전 복습용: Dictionary + Lambda 정렬 활용 문제

d 0_0 b 2026. 6. 28. 16:53

이 문제는 해시(Dictionary)와 정렬(lambda) 을 함께 사용하는 대표 문제다. 그리고 내가 자주하는 실수를 모아놨다.

  1. 장르별 총 재생 수를 구하고
  2. 장르별 노래 목록을 따로 저장한 뒤
  3. 장르는 총 재생 수 기준으로 정렬하고
  4. 각 장르 안에서는 노래를 재생 수 기준으로 정렬해야 한다.

즉, Dictionary로 그룹화하고, lambda로 정렬 기준을 설계하는 문제다.


문제를 보고 떠올려야 하는 패턴

장르별로 묶어야 한다
↓
Dictionary / defaultdict

장르별 총합이 필요하다
↓
defaultdict(int)

장르 안의 노래 목록이 필요하다
↓
defaultdict(list)

정렬 기준이 여러 개다
↓
sorted() + lambda

필요한 자료구조

1. 장르별 총 재생 수

stream_genre = defaultdict(int)

예시:

{
    "classic": 1450,
    "pop": 3100
}

이 자료구조는 장르 순서를 정하기 위해 필요하다.


2. 장르별 노래 목록

stream_song = defaultdict(list)

예시:

{
    "classic": [(0, 500), (2, 150), (3, 800)],
    "pop": [(1, 600), (4, 2500)]
}

여기서 중요한 점은 노래의 재생 수만 저장하면 안 된다는 것이다.

정답에는 노래의 고유 번호가 들어가야 하므로 반드시

(idx, plays[idx])

형태로 저장해야 한다.


전체 코드

from collections import defaultdict

def solution(genres, plays):
    stream_genre = defaultdict(int)
    stream_song = defaultdict(list)
    answer = []

    for idx, genre in enumerate(genres):
        stream_genre[genre] += plays[idx]
        stream_song[genre].append((idx, plays[idx]))

    sorted_stream_genre = sorted(
        stream_genre.items(),
        key=lambda x: x[1],
        reverse=True
    )

    for genre, total_play in sorted_stream_genre:
        songs = sorted(
            stream_song[genre],
            key=lambda x: (-x[1], x[0])
        )

        for idx, play in songs[:2]:
            answer.append(idx)

    return answer

핵심 코드 1: 장르별 총합 구하기

stream_genre[genre] += plays[idx]

이 코드는 장르별 총 재생 수를 누적한다.

classic → 500 + 150 + 800 = 1450
pop     → 600 + 2500 = 3100

장르를 정렬할 때 이 값이 기준이 된다.


핵심 코드 2: 장르별 노래 목록 저장

stream_song[genre].append((idx, plays[idx]))

이 코드는 장르별로 노래를 묶는다.

여기서 (idx, plays[idx])로 저장하는 이유는 두 가지다.

idx       → 정답에 넣어야 하는 고유 번호
plays[idx] → 정렬 기준이 되는 재생 수

핵심 코드 3: 장르 정렬

sorted_stream_genre = sorted(
    stream_genre.items(),
    key=lambda x: x[1],
    reverse=True
)

stream_genre.items()는 이런 형태다.

[("classic", 1450), ("pop", 3100)]

여기서 x는 하나의 튜플이다.

x = ("classic", 1450)
x[0] = "classic"
x[1] = 1450

따라서

key=lambda x: x[1]

총 재생 수 기준으로 정렬하겠다는 의미다.

reverse=True를 사용했기 때문에 총 재생 수가 큰 장르부터 정렬된다.


핵심 코드 4: 장르 안에서 노래 정렬

songs = sorted(
    stream_song[genre],
    key=lambda x: (-x[1], x[0])
)

stream_song[genre]는 이런 형태다.

[(0, 500), (2, 150), (3, 800)]

여기서 x는 하나의 노래 정보다.

x = (0, 500)
x[0] = 고유 번호
x[1] = 재생 수

정렬 조건은 다음과 같다.

1순위: 재생 수가 높은 순
2순위: 재생 수가 같으면 고유 번호가 낮은 순

그래서 정렬 기준은 이렇게 쓴다.

key=lambda x: (-x[1], x[0])

의미는 다음과 같다.

-x[1] → 재생 수 내림차순
x[0]  → 고유 번호 오름차순

이 부분이 이 문제에서 가장 중요한 lambda 활용이다.


핵심 코드 5: 상위 2개만 answer에 넣기

for idx, play in songs[:2]:
    answer.append(idx)

각 장르마다 최대 2곡까지만 수록한다.

songs[:2]는 정렬된 노래 중 앞의 두 개만 가져온다.

정답에는 재생 수가 아니라 고유 번호를 넣어야 하므로

answer.append(idx)

를 사용한다.


이 문제에서 자주 하는 실수

1. 재생 수만 저장하는 실수

stream_song[genre].append(plays[idx])

이렇게 하면 나중에 정답에 넣을 고유 번호를 알 수 없다.

반드시 이렇게 저장한다.

stream_song[genre].append((idx, plays[idx]))

2. lambda 오타

lamba
Ture

가 아니라

lambda
True

다.


3. 변수명 덮어쓰기

for idx, stream_genre in enumerate(genres):

처럼 쓰면 기존 딕셔너리 stream_genre를 덮어쓰게 된다.

반복문 변수는 의미에 맞게 작성한다.

for idx, genre in enumerate(genres):

4. 정렬된 변수명 불일치

sorted_stream_genre = ...

로 만들었으면 아래에서도 같은 이름을 써야 한다.

for genre, total_play in sorted_stream_genre:

정렬 패턴 복습

# value 기준 내림차순
sorted(dic.items(), key=lambda x: x[1], reverse=True)

# 튜플에서 두 번째 값 내림차순, 첫 번째 값 오름차순
sorted(arr, key=lambda x: (-x[1], x[0]))