문제 설명
스트리밍 사이트에서 장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시하려 합니다. 노래는 고유 번호로 구분하며, 노래를 수록하는 기준은 다음과 같습니다.
- 속한 노래가 많이 재생된 장르를 먼저 수록합니다.
- 장르 내에서 많이 재생된 노래를 먼저 수록합니다.
- 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.
노래의 장르를 나타내는 문자열 배열 genres와 노래별 재생 횟수를 나타내는 정수 배열 plays가 주어질 때, 베스트 앨범에 들어갈 노래의 고유 번호를 순서대로 return 하도록 solution 함수를 완성하세요.
제한사항
- genres [i]는 고유번호가 i인 노래의 장르입니다.
- plays [i]는 고유번호가 i인 노래가 재생된 횟수입니다.
- genres와 plays의 길이는 같으며, 이는 1 이상 10,000 이하입니다.
- 장르 종류는 100개 미만입니다.
- 장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.
- 모든 장르는 재생된 횟수가 다릅니다. 입출력 예
genres | plays | return |
---|---|---|
["classic", "pop", "classic", "classic", "pop"] | [500, 600, 150, 800, 2500] | [4, 1, 3, 0] |
입출력 예 설명
classic 장르는 1,450회 재생되었으며, classic 노래는 다음과 같습니다.
- 고유 번호 3: 800회 재생
- 고유 번호 0: 500회 재생
- 고유 번호 2: 150회 재생
pop 장르는 3,100회 재생되었으며, pop 노래는 다음과 같습니다.
- 고유 번호 4: 2,500회 재생
- 고유 번호 1: 600회 재생
따라서 pop 장르의 [4, 1] 번 노래를 먼저, classic 장르의 [3, 0] 번 노래를 그다음에 수록합니다.
풀이
이전에 자바로 풀었지만 채점을 통과 못한 코드를 수정할 겸 코틀린으로 다시 풀어보려 했지만 풀지 못했다. 그래서 참고한 코드를 분석해보려 한다.
import kotlin.math.min
class Solution {
fun solution(genres: Array<String>, plays: IntArray) =
plays.withIndex()
.groupBy { genres[it.index] }
.values.sortedByDescending { v -> v.sumOf { it.value } }
.map { v -> v.sortedByDescending { it.value }.map { it.index } }
.fold(intArrayOf()){acc, v -> acc + v.subList(0, min(2, v.size))}
}
정말 아름다운 함수형 코드이다.
withIndex
withIndex()는 (index, value) 프로퍼티를 가진 IndexedValue()로 반환하는 함수이다.
-출력결과
IndexedValue(index=0, value=500) IndexedValue(index=1, value=600) IndexedValue(index=2, value=150) IndexedValue(index=3, value=800) IndexedValue(index=4, value=2500)
groupBy
두 값을 묶어서 Map형태로 반환하는 함수이다. 블록 안에 묶어줄 조건(key)을 명시하며, 조건에 만족하는 원소(value)들의 리스트(Iterable <T>)를 반환한다.
위 코드에선 장르별로 플레이 횟수를 리스트 형태로 반환한다. 장르 배열에 접근할 때 사용한 index가 IndexedValue()의 프로퍼티이다.
-출력결과
classic=[IndexedValue(index=0, value=500), IndexedValue(index=2, value=150), IndexedValue(index=3, value=800)] pop=[IndexedValue(index=1, value=600), IndexedValue(index=4, value=2500)]
values
IndexedValue()의 프로퍼티, [장르, 재생수 리스트]에서 재생수 리스트만 가져온다.
sortedByDecending
이름에서도 알 수 있듯이 블락에 있는 조건으로 내림차순 정렬하는 함수이다. sortedBy로 오름차순 정렬도 가능하다.
블락에 있는 조건을 보면 [장르, 재생수 리스트] 맵의 재생수 리스트를 가져온다. sumOf함수로 재생수 리스트의 재생 횟수의 합을 구해서 속한 노래가 많이 재생된 장르가 먼저 나오도록 정렬한다.
-출력결과
[IndexedValue(index=1, value=600), IndexedValue(index=4, value=2500)] [IndexedValue(index=0, value=500), IndexedValue(index=2, value=150), IndexedValue(index=3, value=800)]
map
filter함수가 리스트와 맵 같은 Collection함수에서 조건에 맞는 원소를 걸러내는 함수였다면, map함수는 조건에 따라 일정한 연산을 해주는 함수이다.
현재까지 결과를 보면 Map <String, List <IndexedValue <Int>>>구조를
가지고 있다. 불필요한 IndexedValue를 없앨 필요가 있다.
따라서 sortedByDecending으로 재생수가 많이 재생된 순서대로 정렬해준다. 정렬된 List <IndextedValue <Int>>에서
map함수를 통해 index리스트를 반환해준다.
결과로 장르내에서 많이 재생된 노래 순서 리스트를 얻을 수 있다.
-출력결과
[4, 1] [3, 0, 2]
fold
종이를 접는 것처럼 리스트의 왼쪽부터 마지막까지 블록 연산을 적용하는 함수이다.
acc는 누적된 결과이고 v는 장르내 많이 재생된 노래 순서 리스트이다. 장르별로 2개가 최대이기 때문에 subList로 자르는 과정이 필요하다. 길이가 2보다 적을 수 있기 때문에 2와 리스트 길이의 최솟값을 구해준다. 결괏값이 IntArray이므로 형 변환을 해준다.
-출력결과
4 1 3 0
맵을 이용한 단순 정렬 문제를 풀 수 없었다는 것에 많이 충격받았다. 더 열심히 알고리즘을 공부해야겠다는 생각밖에 안 든다.
'코딩테스트 > 프로그래머스' 카테고리의 다른 글
JadenCase 문자열 만들기(Lv.2) (1) | 2022.09.15 |
---|---|
최댓값과 최솟값(Lv.2) (1) | 2022.09.14 |
카펫 (0) | 2022.06.13 |
소수찾기 (0) | 2022.06.13 |
여행경로 (0) | 2022.06.13 |