[Algorithm/JavaScript] 방금그곡

Dico·2020년 12월 31일
0

[Algorithm/JavaScript]

목록 보기
14/18

Programmers Level.2 Question Review 📓


문제출처: https://programmers.co.kr/learn/courses/30/lessons/17683

문제

문제 보러가기


제출답안

/*
한 요소당 '시작시간, 끝나는시간, 음악제목, 악보정보' 순서로 들어온다.
음악길이 > 재생된 시간 = 재생시간만큼만 재생
음악길이 < 재생된 시간 = 처음부터 반복해서 재생
1. 재생된 시간(길이; playingTime)구하기
2. actualPlayingInfo라는 변수에 실제 재생되었던 멜로디를 playingTime의 길이만큼 채우기
3. actualPlayingInfo가 m을 가지고 있는지 확인해야함.
*/
function solution(m, musicinfos) {
    let answer;
    let allPassedMusic = [];
    musicinfos.forEach(el => {
        let passedMusic = []; //[playingTime, 음악이름]
        let curStr = el.split(",");//, 단위로 쪼개진 배열로 받기. [ '12:00', '12:14', 'HELLO', 'CDEFGAB' ]
        const playingTime = getPlayingTime(curStr[0], curStr[1]); //14
        const actualPlayingInfo = fillMusicNote(new Array(playingTime), curStr[3]);
        const hasM = checkMusicNote(actualPlayingInfo, m);
        if (hasM) {
            answer = curStr[2];
            passedMusic.push(playingTime);
            passedMusic.push(curStr[2]);
            allPassedMusic.push(passedMusic);
        }
    })

    if (!answer) {
        answer = `(None)`;
    } else if (allPassedMusic.length >= 2){//조건이 일치하는 음악이 여러 개일 때에는 라디오에서 재생된 시간이 제일 긴 음악 제목을 반환한다. 재생된 시간도 같을 경우 먼저 입력된 음악 제목을 반환한다.
        let longestMusic;
        let longestPlayingTime = 0;
        allPassedMusic.forEach(el => {
            if (el[0] > longestPlayingTime) { //조건에 부합하는 음악이 2개이상일 때
                longestPlayingTime = el[0];//el = [playingTime, 음악이름]
                longestMusic = el[1];
            } else if (el[0] === longestPlayingTime){ //조건에 부합하는 음악의 재생 시간들까지 같다면, 먼저 입력된 음악이 longestMusic.
            }
        });
        answer = longestMusic;
    }
    return answer;
}

function getPlayingTime (start, end){//'12:00', '12:14'
    let min;
    let hr = end.substring(0, 2) - start.substring(0, 2);
    if (hr > 0){ //시 단위가 0보다 클 때, 시를 분으로 바꾸어 분과 합치는 작업 필요.
        const endMin = (end.substring(0, 2))*60 + end.substring(3)*1; //📍📍📍
        const startMin = (start.substring(0, 2))*60 + start.substring(3)*1; //📍📍📍
        min = endMin - startMin;
    } else {
        min = end.substring(3) - start.substring(3);
    }
    return min;
}

function fillMusicNote (emptyArr, str){
    let idx = 0;
    let playingNote = str.split(""); //#이 있는 경우에 앞전 요소와 합치고 앞전 요소는 빼야함.

    playingNote.forEach((el, idx) => {
        if (el === '#') {
            el = `${playingNote[idx - 1]}` + `${el}`;
            playingNote.splice(idx - 1, 2, el);
        }
    })

    for (let i = 0; i < emptyArr.length; i++){
        let curLetter = playingNote[i];
        if (!curLetter){//악보정보가 실제실행시간보다 짧을 때. 처음부터 다시 실행되어야 함.
            if (idx >= playingNote.length) {idx = 0};
            curLetter = playingNote[idx];
            idx++;
        }
        emptyArr[i] = curLetter;
    }
    return emptyArr;
}

function checkMusicNote (musicNote, melody){
    //boolean으로 결과 반환. musicNote는 array로 들어오고, melody는 string으로 들어온다.
    let arrMelody = melody.split("");
    arrMelody.forEach((el, idx) => {
        if (el === '#') {
            el = `${arrMelody[idx - 1]}` + `${el}`;
            arrMelody.splice(idx - 1, 2, el);
        }
    });

    let melLength = arrMelody.length;
    for(let i = 0; i < musicNote.length; i++){
        if(musicNote[i][0] === melody[0]){
            //melody의 길이만큼 musicNote의 요소들을 합쳐서 string으로 비교하기
            const tempStr = musicNote.slice(i, i + melLength).join("");
            if (tempStr === melody) {
                return true;
            }
        }
    };
    return false;
}

+ 추가로 만든 테스트 케이스:

solution("ABCDEFG", ["12:00,12:14,HELLO,CDEFGAB", "13:00,13:05,WORLD,ABCDEF"]) //"HELLO"
solution("CC#BCC#BCC#BCC#B", ["03:00,03:30,FOO,CC#B", "04:00,04:08,BAR,CC#BCC#BCC#B"]) //"FOO"
solution("ABC", ["12:00,12:14,HELLO,C#DEFGAB", "13:00,13:05,WORLD,ABCDEF"]) //"WORLD"
solution("ABC", ["12:00,12:14,HELLO,C#DEFGAB", "13:00,13:05,WORLD,BCDEF"]) //"(None)"
solution("ABC", ["12:00,12:14,DROPTHEBEAT,ABCDEF", "12:00,12:14,VIVA,ABCDEF", "12:00,12:14,HELLO,ABCDEF"]) //"DROPTHEBEAT"
solution("A#B", ["12:00,12:03,a,BA#BB#"])//"a"
solution("A#B", ["12:00,12:03,a,A#B","12:00,12:08,b,A#B"]) //"b"
solution("A#BC#DFFF", ["12:00,12:07,a,A#BC#DFFF", "12:00,12:06,b,A#BC#"]) //"a"
solution("AAAA", ["12:00,12:05,a,AAAA", "12:00,12:05,b,AAAA"]); //"a"
solution("AAAA", ["12:00,14:05,a,AAAA", "12:00,12:06,b,AAAA"]); //"b"

오늘의 Lesson

  • 아래 보여지는 로직은 sort()정렬을 하면 더욱 간단해진다.
//수정 전
    if (!answer) {
        answer = `(None)`;
    } else if (allPassedMusic.length >= 2){
         let longestMusic;
         let longestPlayingTime = 0;
         allPassedMusic.forEach(el => {
             if (el[0] > longestPlayingTime) { //조건에 부합하는 음악이 2개이상일 때
                 longestPlayingTime = el[0];//el = [playingTime, 음악이름]
                 longestMusic = el[1];
             } else if (el[0] === longestPlayingTime){ //조건에 부합하는 음악의 재생 시간들까지 같다면, 먼저 입력된 음악이 longestMusic.
             }
         });
 	answer = longestMusic;
    }
//----------------------------------------------------------------------------------------------------------
//수정 후
    if (!answer) {
        answer = `(None)`;
    } else if (allPassedMusic.length >= 2){
        allPassedMusic.sort((a, b) => b[0] - a[0]);
        answer = allPassedMusic[0][1];
    }
  • 이 문제 역시 기본으로 주어지는 테스트 케이스만으로는 허점을 찾을 수가 없었다. 있을 법한 문제상황을 떠올려서 테스트 케이스를 추가로 만들어 실행해보는 과정이 필요했다.
  • 가장 크게 헤매였던 부분은 📍📍📍 표시가 된 부분이었다.
    *1 을 하지 않은 상태에서는 substring의 결과가 String타입으로 반환되어 84 + 005가 된다. 즉, 원하는 결과는 845인데 84005를 반환 받게 되는 것이다.
    그래서 뒤에 더해주는 end.substring(3)에도 1을 곱해줌으로써 숫자형으로 강제타입변환 후 덧셈을 진행한다. 항상 연산하는 값들의 타입을 생각할 것!! (중요⭐️⭐️⭐️)
const endMin = (end.substring(0, 2))*60 + end.substring(3)*1; //📍📍📍
const startMin = (start.substring(0, 2))*60 + start.substring(3)*1; //📍📍📍
  • 또한 #이 붙는 요소(예. C#)들이 있어 단순히 split("")을 통해 문자열을 배열형태로 바꾸는 것 뿐만 아니라 앞전 알파벳과 #기호를 합쳐주는 과정이 필요했다. 설계시에는 생각하지 못했던 부분이라 수정하는 데에 시간이 꽤나 소요되었다.
  • 다른 문제보다 유독 테스트 케이스가 많아 시간도 역대급으로 걸리고 애를 먹었지만, 다행스럽게도 팀원들의 도움을 받아서 어렵게 통과했다.(여전히 팀원들의 도움 없이는 다양한 테스트 케이스를 생각해내는 것이 어렵다 🥺)
    문제는 기능별로 함수를 분리했더니 코드 길이가 무지막지하게 길어졌다는 것이다.
    직관적인 코드를 짜려고 분리한건데 길이가 길어지니 결국 가독성도 좋지가 않은 느낌이다. 뿐만 아니라 조건문도 많아서 인덴테이션도 굉장하다... 클린코드 작성하는 방법을 많이, 아주 많이 공부해야할 것 같다. 💦
profile
프린이의 코묻은 코드가 쌓이는 공간

0개의 댓글