# [해시][Lv3] 베스트앨범 - 자바스크립트
# 프로그래머스 문제
https://programmers.co.kr/learn/courses/30/lessons/42579?language=javascript# (opens new window)
# 전체 코드
function solution(genres, plays) {
var answer = [];
const genresMap = new Map();
const playsMap = new Map();
for(var i=0; i<genres.length; i++){
var isExist = genresMap.get(genres[i]);
if(isExist){ // 이미 장르가 저장 된 경우
genresMap.set(genres[i], plays[i]+isExist);
playsMap.get(genres[i]).set(i, plays[i]);
} else { // 새로 장르가 들어 가는 경우
genresMap.set(genres[i], plays[i]);
playsMap.set(genres[i], new Map( [[i, plays[i]]] ));
}
}
// 1. 속한 노래가 많이 재생된 장르 genresMap 정렬
// map -> object -> array -> 정렬 -> obejct
let genresObjet = Object.fromEntries(genresMap);
let genresObjet_sorted = Object.fromEntries(
Object.entries(genresObjet).sort((a,b) => b[1]-a[1])
);
// 2. 장르별 가장 많이 재생된 노래 2개 고르기 playsMap
Object.keys(genresObjet_sorted).forEach(v => {
// 1. 해당 장르(v)의 재생횟수를 오름차순으로 나열
var playObjet = Object.fromEntries(playsMap.get(v));
var playObjet_sorted = Object.entries(playObjet).sort((a,b) => b[1]-a[1]);
// 2. 상위 2개 선택
var loopCnt = playObjet_sorted.length < 2 ? 1 : 2;
for(var i=0; i<loopCnt; i++){
answer.push(parseInt(playObjet_sorted.shift()[0]));
}
})
return answer;
}
# 시도 1. 기본 틀 잡기
일단 해시 관련 문제라는 걸 알고있음 ⇒ map을 써야겠다 로 이어지니 첫 시작은 쉬웠음.
주어진 genres, plays배열을 이용해 해시맵 두개를 만든다.
const genresMap = new Map(); const playsMap = new Map(); for(var i=0; i<genres.length; i++){ var isExist = genresMap.get(genres[i]); if(isExist){ // 이미 장르가 저장 된 경우 genresMap.set(genres[i], plays[i]+isExist); playsMap.get(genres[i]).set(i, plays[i]); } else { // 새로 장르가 들어 가는 경우 genresMap.set(genres[i], plays[i]); playsMap.set(genres[i], new Map( [[i, plays[i]]] )); } }
genresMap : {key, value} ⇒ {장르명, plays 장르별로 합산한 값}
playsMap : {key, value} ⇒ {장르명, map(idx, plays) }
⇒ playsMap의 경우 value에 map을 한번 더 사용해줬다.
밑처럼 생각했으나
장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.
규칙때문에 그냥 idx, plays 순으로 넣었다. 정렬은 genresMap를 정렬한 것 처럼 정렬함.Map.keys()
배열을 정렬하기 위해서 실행횟수(plays)를 키로 둔다. value 자리에 넣어도Map.values()
배열을 얻어서 정렬할 수 있긴하다. 하지만 가장 큰 값 2개를 얻은 후, 그 값의 고유번호(map(plays, idx)의 idx)를 얻기 위해선 key에 넣어두고playMap.get(장르명).get(구해진 큰 값)
로 구하는 게 깔끔하다.
genresMap을 재생수 순으로 정렬한다.
let genresObjet = Object.fromEntries(genresMap); let genresObjet_sorted = Object.fromEntries( Object.entries(genresObjet).sort((a,b) => b[1]-a[1]) );
Map 형식의 데이터를 sort 함수를 사용하기 위해선 array 형식으로 바꿔야한다.
⇒ 이 부분에서
forEach
를 사용해 배열로 만든 후 정렬할까 했지만.. 하나하나 하는 것보다 내장함수(?)를 사용해 보고 싶어서 검색을 했다.es10
에 새로 나온 함수를 발견했고Map → Object → Array → 정렬 → Object
순으로 진행했다. (사실 object로 변환을 한번 더 하지 않아도 되고 array[0]이렇게 사용해도 되지만.. 깔끔하게 가기위해..)Object.fromEntries(iterable)
: 반복가능한 키값 쌍의 목록을 객체로 반환(프로토타입이 Object)Object.entries(obj)
: [key, value] 쌍의 배열을 반환 (프로토타입이 Array)+ 콘솔에서 확인해보기. > genresMap Map(2) {"classic" => 1450, "pop" => 3100} __proto__: Map > Object.fromEntries(genresMap) {classic: 1450, pop: 3100} __proto__: Object > Object.entries(genresMap) [] __proto__: Array(0) > Object.entries(Object.fromEntries(genresMap)); (2) [Array(2), Array(2)] 0: (2) ["classic", 1450] 1: (2) ["pop", 3100] __proto__: Array(0)
정렬된 genresMap를 for문으로 돌린다. for문안에서 장르별 많이 재생된 곡의 고유번호는 playsMap의 value에 위치하는 map을 이용한다.
Object.keys(genresObjet_sorted).forEach(v => { // 1. 해당 장르(v)의 재생횟수를 오름차순으로 나열 var playObjet = Object.fromEntries(playsMap.get(v)); var playObjet_sorted = Object.entries(playObjet).sort((a,b) => b[1]-a[1]); // 2. 상위 2개 선택 for(var i=0; i<2; i++){ answer.push(parseInt(playObjet_sorted.shift()[0])); } })
# 결과
모든 테스트 케이스를 통과하지 못함! 당연하다. 왜냐면 일단 기본 틀을 잡는 데 주력하고, 문제에 주어진 예외사항들을 처리 안했음..
# 시도 2. 제한사항 처리하기
장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.
상위 2개 선택하는 부분에
loopCnt
변수를 둔다.// 2. 장르별 가장 많이 재생된 노래 2개 고르기 playsMap Object.keys(genresObjet_sorted).forEach(v => { // 1. 해당 장르(v)의 재생횟수를 오름차순으로 나열 var playObjet = Object.fromEntries(playsMap.get(v)); var playObjet_sorted = Object.entries(playObjet).sort((a,b) => b[1]-a[1]); // 2. 상위 2개 선택 var loopCnt = playObjet_sorted.length < 2 ? 1 : 2; for(var i=0; i<loopCnt; i++){ answer.push(parseInt(playObjet_sorted.shift()[0])); } })
정확성 테스트 테스트 1 〉 통과 (0.24ms, 30MB) 테스트 2 〉 통과 (0.25ms, 30.1MB) 테스트 3 〉 통과 (0.29ms, 30MB) 테스트 4 〉 통과 (0.19ms, 29.9MB) 테스트 5 〉 통과 (0.42ms, 30.1MB) 테스트 6 〉 통과 (0.42ms, 30MB) 테스트 7 〉 통과 (0.34ms, 30.1MB) 테스트 8 〉 통과 (0.32ms, 30MB) 테스트 9 〉 통과 (0.26ms, 30.3MB) 테스트 10 〉 통과 (0.48ms, 29.9MB) 테스트 11 〉 통과 (0.34ms, 30MB) 테스트 12 〉 통과 (0.39ms, 30.1MB) 테스트 13 〉 통과 (0.42ms, 30MB) 테스트 14 〉 통과 (0.42ms, 30.1MB) 테스트 15 〉 통과 (0.24ms, 30.2MB) 채점 결과 정확성: 100.0 합계: 100.0 / 100.0
끝! 개인적으로 level3 문제임에도 불구하고 이전에 풀었던 해시 문제(level1, 2)보다 가장 해시답고(?) 쉽고 재밌었다.. 이상한 수학 공식도 몰라도 되고....
다른 사람의 답변을 둘러보니 일단 나는
sort
함수의 동작 원리를 더 정확히 이해할 수 있도록 공부해야 할 것 같다.