programmers

[Programmers Level 3] 베스트 앨범 Javascript

2023. 1. 8. 16:45
목차
  1. ⚡문제정보
  2. 🙄주의사항
  3. 🔍접근방법
  4. 🔍나의 풀이
  5. 🔍테케로 보는 스피드웨건
  6. 🔍다른 사람의 멋진 풀이

⚡문제정보

 

처음으로 제 힘으로 푼 3레벨 문제입니다만..

정답률이 50%나 되는 3레벨 중엔 쉬운 문제네용..

물론 저는 쉽게 못 풀었습니다.

 

문제를 풀면서 주의할 점은 다음과 같습니다.


🙄주의사항

1. 각 장르별로 가장 많이 플레이 된 곡을 2개씩 뽑아와야합니다.
** 이때 장르에 곡이 한개밖에 없는 경우도 존재합니다.

2. 각 장르에 속한 곡의 모든 플레이 수를 합산하여 가장 플레이수가 많은 순서대로 나열해야합니다.
** 단일 곡의 플레이수를 기준으로 정렬해선 안됩니다.

3.고유번호는 배열 인덱스입니다.

4. 합산플레이가 많은 장르 순서대로 곡을 나열해야합니다.
** 각 장르별 가장 플레이수가 많은 곡을 찾기위한 정렬(1)과
합산플레이가 많은 장르 순서대로 나열하기 위한 정렬(2)
 즉 정렬이 두번 필요합니다.

🔍접근방법

1. 각 장르의 총 플레이수의 합산을 얻어야 할 것입니다.

2. 정확한 분류를 위해 각 곡들의 인덱스, 장르, 플레이수를 알아야 할 것입니다.

3. 2에서 얻은 분류를 토대로 각 장르의 최다플레이곡 2개를 순서대로 뽑아옵니다.

4. 1에서 얻은 분류를 토대로 각 장르의 총플레이순서로 정렬시켜주고 idx만을 담은 배열로 리턴합니다. 


🔍나의 풀이

 

function solution(genres, plays) {
    let map = new Map()
    let set = [...new Set(genres)]
    let result = []
    let playCounter = new Map()
    
    for(i=0 ; i<set.length ; i++) {
        map.set(set[i],[])
    }
    
    for(i=0; i<genres.length; i++) {
        let pusher = map.get(genres[i])
        pusher.push(
        {
            genre : genres[i],
            idx : i,
            playCnt : plays[i]
            
        }
        )
        playCounter.set(genres[i], (playCounter.get(genres[i]) || 0)  + plays[i] )
        
    }
    
    [...map].forEach(ele => {
        ele[1].sort((a,b) => b.playCnt - a.playCnt)
        if(ele[1].length > 1) {
            result.push([ele[1][0],ele[1][1]])
        }
        else result.push([ele[1][0]])
        
    })
    
    result.sort((a,b) => playCounter.get(b[0].genre) - playCounter.get(a[0].genre) )
    
    return result
        .map(ele => ele.map(answer => answer.idx))
        .reduce((acc,cur) => acc.concat(cur))

}

 

나름... 가독성있게 코드를 작성하려고 노력했는데

다른 사람이 보기에는 어떨지 모르겠네요

 

function solution(genres, plays) {
    let map = new Map()
    // 모든 값을 저장할 Map입니다.
    let set = [...new Set(genres)]
    // 각 장르를 set을 통해 중복없게 만들어줍니다.
    let result = []
    // 정답을 반환할 배열
    let playCounter = new Map()
    // 장르별 총 플레이합산을 구하기 위한 Map입니다.
    
    for(i=0 ; i<set.length ; i++) {
        map.set(set[i],[])
        // map의 밸류값으로 배열을 설정해줌
        // 이 작업을 위해 위에서 set을 선언한것
        // Map자료구조의 밸류값으로 배열을 두고 조작하기 위해 이 for문을 사용함
    }
    
    for(i=0; i<genres.length; i++) {
        let pusher = map.get(genres[i])
        // 변수를 선언하고 변수에 get한 밸류값을 할당해줌
        
        pusher.push(
        {
            genre : genres[i],
            idx : i,
            playCnt : plays[i]
            
        }
        )
        //get 값에 genre,idx,playCnt를 담은 객체를 push해줍니다.
        
        playCounter.set(genres[i], (playCounter.get(genres[i]) || 0)  + plays[i] )
        // playCounter에 각 장르별 총플레이수를 합산시켜줍니다.
        
    }
    
    [...map].forEach(ele => {
    // map에 forEach를 돌립니다.
    // 굳이 전개하지않아도 Map자료구조에 바로 forEach를 할수있긴합니다만...
    // 일단 익숙한 형태로 하기위해 배열로 바꿔준 뒤 forEach를 돌렸습니다.
    
        ele[1].sort((a,b) => b.playCnt - a.playCnt)
        // b.객체키값을 통해 객체를 특정 키값을 기준으로 정렬시킬수있습니다.
        // 이때 ele[0]은 장르값이고 ele[1]은 배열안에 객체들이 담긴 형태로 있습니다.
        // 따라서 그 객체들을 playCnt기준으로 정렬해주는 작업입니다.
        
        if(ele[1].length > 1) {
            result.push([ele[1][0],ele[1][1]])
        }
        //장르의 곡이 단 한개일수도 있기때문에 if문으로 예외처리를 해줍니다.
        //각 장르마다 제일 많이 플레이된 곡 2개를 순서대로 배열안에 넣은 형태로
        //result 배열에 push합니다
        
        else result.push([ele[1][0]])
        //곡이 단 한개면 그 곡을 result 배열에 push 합니다.
        
    })
    
    result.sort((a,b) => playCounter.get(b[0].genre) - playCounter.get(a[0].genre) )
    // 각 장르의 최다플레이수를 get으로 가져와서 get값을 기준으로 정렬해줍니다.
    
    return result
        .map(ele => ele.map(answer => answer.idx))
        .reduce((acc,cur) => acc.concat(cur))
        // map을 두번 사용해서 각 객체들을 idx만 남은 형태로 만들어준다음
        // reduce를 이용해 2차원배열 형태인 값들을 1차원배열로 만들어주고 리턴합니다.

}

 

이뭔씹인데 콘솔찍으면서 각 과정에서의 흐름을 보면... 이해가 잘 될거에요...

이미 각 과정흐름을 봐야하는 시점에서 가독성 망한건가요? ㅈㅅ..

 

최근에 다시 풀어봤습니다.

function solution(genres, plays) {
    const obj = {};
    const maxGenre = new Map();
    const answer = [];
    
    genres.reduce((obj, genre, idx) => {
      Array.isArray(obj[genre])
        ? obj[genre].push([idx, plays[idx]])
        : (obj[genre] = [[idx, plays[idx]]]);
      maxGenre.set(genre, (maxGenre.get(genre) || 0) + plays[idx]);
      return obj;
    }, obj);

    for (key in obj) {
      obj[key]?.sort((a, b) => b[1] - a[1]);
    }
    
    const sorted = [...maxGenre].sort((a, b) => b[1] - a[1]);
    for (let i = 0; i < sorted.length; i++) {
      for (let j = 0; j < 2; j++) {
        let key = sorted[i][0];
        if(obj[key].length < 2 && j > 0) break
        answer.push(obj[key][j][0]);
      }
    }
    
    return answer
}

key값을 두개씩 넣어주는 과정에서 장르에 담긴 곡이 한개밖에 없는 케이스를 고려해줘야합니다.

고부분을 고려해주지않으면 런타임에러가 발생해요


🔍테케로 보는 스피드웨건

	["classic", "pop", "classic", "classic", "pop"], [500, 600, 150, 800, 2500]

위 테스트케이스를 기준으로 코드를 보겠읍니다..

 

    for(i=0; i<genres.length; i++) {
        let pusher = map.get(genres[i])
        pusher.push(
        {
            genre : genres[i],
            idx : i,
            playCnt : plays[i]
            
        }
        )
        playCounter.set(genres[i], (playCounter.get(genres[i]) || 0)  + plays[i] )
    }

이 코드의 실행이 완료된 시점에서

map과 playCounter의 상태는 다음과 같습니다.

 

map => Map(2) {
  'classic' => [
    { genre: 'classic', idx: 0, playCnt: 500 },
    { genre: 'classic', idx: 2, playCnt: 150 },
    { genre: 'classic', idx: 3, playCnt: 800 }
  ],
  'pop' => [
    { genre: 'pop', idx: 1, playCnt: 600 },
    { genre: 'pop', idx: 4, playCnt: 2500 }
  ]
}
// map의 상황

playCounter => Map(2) { 'classic' => 1450, 'pop' => 3100 }
// playCounter의 상황

각 장르를 기준으로 객체에 값이 잘 담긴 모습을 확인할 수 있읍니다.

키 : 밸류형태에서 

밸류가 [객체,객체,객체] 형태로 담겨있는 구조네용

이제 이걸 forEach합니다.

    [...map].forEach(ele => {
        ele[1].sort((a,b) => b.playCnt - a.playCnt)
        if(ele[1].length > 1) {
            result.push([ele[1][0],ele[1][1]])
        }
        else result.push([ele[1][0]])
        console.log(ele)
    })

이 forEach는 총 2번 도는데 classic에 먼저 접근하고 pop에 접근한 다음 순회를 마칠것입니다.

ele[1]로 밸류에 접근한다음 각 배열을 playCnt를 기준으로 정렬합니다.

그리고 if문에 맞게 result에 push해주면 이때 상태는 이렇습니다.

 

// [...map]의 상태

[
  'classic',
  [
    { genre: 'classic', idx: 3, playCnt: 800 },
    { genre: 'classic', idx: 0, playCnt: 500 },
    { genre: 'classic', idx: 2, playCnt: 150 }
  ]
]
[
  'pop',
  [
    { genre: 'pop', idx: 4, playCnt: 2500 },
    { genre: 'pop', idx: 1, playCnt: 600 }
  ]
]

// result의 상태

	[
  [
    { genre: 'classic', idx: 3, playCnt: 800 },
    { genre: 'classic', idx: 0, playCnt: 500 }
  ],
  [
    { genre: 'pop', idx: 4, playCnt: 2500 },
    { genre: 'pop', idx: 1, playCnt: 600 }
  ]
]

각 장르의 최다플레이수를 기준으로 정렬되고 

result에 상위 2곡이 순서대로 push된걸 확인할 수 있군용

[ [객체모음 ], [객체모음] ]과 같이 2차원 배열 형태로 result가 구성됩니다.

 

    result.sort((a,b) => playCounter.get(b[0].genre) - playCounter.get(a[0].genre) )
    
    return result
        .map(ele => ele.map(answer => answer.idx))
        .reduce((acc,cur) => acc.concat(cur))

이제 get값을 기준으로 정렬해주고

map을 두번 사용해서 2차원 배열에 담긴 객체들을 고유번호(idx)로 변환해줍니다.

map을 두번 거친 후 상태는 다음과 같습니다.

[[4,1],[3,0]]

값 자체는 맞는데 2차원 배열 형태로 값이 담겨졌네요

이걸 이제 concat으로 합쳐주면 1차원 배열이 되면서 원하는 값을 얻습니다.


 

🔍다른 사람의 멋진 풀이

 

function solution(genres, plays) {
    var dic = {};
    genres.forEach((t,i)=> {
        dic[t] = dic[t] ?  dic[t] + plays[i] :plays[i];        
    });

    var dupDic = {};
    return genres          
          .map((t,i)=> ({genre : t, count:plays[i] , index:i}))
          .sort((a,b)=>{               
               if(a.genre !== b.genre) return dic[b.genre] - dic[a.genre];
               if(a.count !== b.count) return b.count - a.count;
               return a.index - b.index;
           })
           .filter(t=>  {
               if(dupDic[t.genre] >= 2) return false;
               dupDic[t.genre] = dupDic[t.genre] ? dupDic[t.genre]+ 1 : 1;
               return true;
            })
           .map(t=> t.index);    
}

genres.forEach((t,i))로 plays의 인덱스까지 접근하면서

단 한줄의 삼항연산자로 각 장르의 총합플레이를 세어서 dic에 저장해주네용

 

그러고 genres에 map을해서 

각 요소를 genre , 재생횟수, 고유번호를 담은 객체로 바꿔주고

sort를 하는데 sort 부분은 잘 이해가 안되네용... 따흑흑

 

반응형
저작자표시 비영리 (새창열림)

'programmers' 카테고리의 다른 글

[Programmers Level 2] 할인 행사 Javascript  (0) 2023.01.11
[Programmers Level 2] 괄호 회전하기 Javascript  (0) 2023.01.11
[Programmers Level 2] H-index Javascript  (0) 2022.12.31
[Programmers Level 1] 문자열 나누기 Javascript  (0) 2022.12.30
[Programmers Level 2] 짝지어 제거하기 Javascript  (0) 2022.12.27
  1. ⚡문제정보
  2. 🙄주의사항
  3. 🔍접근방법
  4. 🔍나의 풀이
  5. 🔍테케로 보는 스피드웨건
  6. 🔍다른 사람의 멋진 풀이
'programmers' 카테고리의 다른 글
  • [Programmers Level 2] 할인 행사 Javascript
  • [Programmers Level 2] 괄호 회전하기 Javascript
  • [Programmers Level 2] H-index Javascript
  • [Programmers Level 1] 문자열 나누기 Javascript
냠냠맨
냠냠맨
프론트엔드 개발 전반을 다루는 기술 블로그입니다.
React와 TypeScript를 좋아하는 개발자프론트엔드 개발 전반을 다루는 기술 블로그입니다.
냠냠맨
React와 TypeScript를 좋아하는 개발자
냠냠맨
전체
오늘
어제
  • all category (433)
    • CMC (0)
    • best (11)
    • 년간회고 (1)
    • cheetsheet (15)
    • 프로젝트 회고 (3)
    • 서평 (3)
    • SEO Study (1)
    • 프로젝트 진행기 (10)
    • testcode (9)
    • yarnberry (7)
    • css (21)
    • typescript (15)
    • redux (7)
    • react (43)
    • Next.js (9)
    • Nestjs (3)
    • javascript (44)
    • programmers (67)
    • leetcode (41)
    • frontend (41)
    • backjoon (1)
    • Next.js Beta Docs 번역 (12)
    • TIL (15)
      • html (3)
    • Network (12)
      • 간단 정리 시리즈 (2)
      • 질답 준비 (0)
    • 자료구조와 알고리즘 (2)
    • CS (4)
      • OS (1)
    • 취업준비 (2)
    • zoom websocket (2)
    • talk (6)
    • 면접대비 (1)
    • 코드스테이츠 프론트 (5)
    • 간헐적 회고 (17)

블로그 메뉴

  • leetcode
  • programmers
  • javascript
  • html
  • css

공지사항

인기 글

태그

  • 프론트엔드
  • 말풍선
  • 개발자
  • 개발
  • 테오의스프린트
  • 주니어개발자
  • JavaScript
  • 테오의스프린트17기
  • CSS
  • LeetCode
  • teosprint
  • border말풍선
  • frontend
  • 코드스테이츠 #프론트엔드

최근 댓글

최근 글

hELLO · Designed By 정상우.
냠냠맨
[Programmers Level 3] 베스트 앨범 Javascript
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.