ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 항해 99 5기 TIL_8
    항해 99 2022. 1. 18. 02:05

    ▶ Today I Learned

    <알고리즘 기초>

     

    [문자열 내 p와 y의 개수]

     

    https://programmers.co.kr/learn/courses/30/lessons/12916

    소요시간: 18분 42초

     

    처음 짰던 코드

    function solution(s){
        
    let upperS= s.toUpperCase()
    let i = 0
    let countP = 0
    let countY = 0
    
    var arr = [...upperS] // 이렇게하면 문자열이 하나씩 나눠지는 배열이 된다.
    // 물론 반복문을 써서 하나하나 집어넣어도 되고 Array.from이라는 방법도 있다.
    //여기서 사용한 방법은 확산연산자이다.
    
    for (i ; i < arr.length ; i++) {
    
      arr[i] == 'P' ? countP++ : arr[i] == 'Y' ? countY++ : countY
      // P 카운팅, Y 카운팅
    }
    return (countP == 0 && countY == 0 ? true : countP == countY ? true : false)
    // 둘다 포함되어 있지 않으면 true 반환, 되어있다면 둘의 카운팅 수가 같은지 비교하여 같으면 true 아니면 false
    }

    다른 방법은 없을까?

     

    문자열.match()는 파라미터 안의 문자가 문자열에 포함되어 있으면 그 값을 반환해준다.

    파라미터에는 정규식이 들어가며 정규식 객체가 아닐 경우 new Regexp(찾는문자)를 활용해 자동으로 정규식화 해준다.

    단, 여러 글자가 있어도 가장 처음 찾은 값에 대해서만 반환해준다.

    그 형태는 [ 'P', index: 0, input: 'PPOOOYY', groups: undefined ]와 같다.

    여기서 match(/검색문자/gi)를 해주면 대소문자를 구분하지 않고 찾아주며 그 값을 문자열 배열로 반환해준다.

     

    g 는 글로벌, i 는 ignore로 전역에 걸쳐서 대소문자여부는 무시하고 값을 찾아 배열로 반환해준다.

     

    출처:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match

     

    이를 활용한다면

     

    function solution(s){
        
    return s.match(/p/gi).length == 0 && s.match(/y/gi).length == 0 ? true : s.match(/p/gi).length == s.match(/y/gi).length ? true : false
    
    }

    라고 한 줄로 표현할 수도 있다!

    와! 그럼 깔끔히 해결?

     

    은 아니다.

     

    이 경우 문자열에 p나 y가 하나도 없을 때

    이렇게 배열자체가 생성되지 않아 null 값이 되어버린다.

     

    프로그래머스 모범답안

    function numPY(s){
      //함수를 완성하세요
        return s.toUpperCase().split("P").length === s.toUpperCase().split("Y").length;
    }
    

     

    와우.. split()을 사용하면 해당 seperator 기준으로 배열이 나뉜다.

    즉, 인자가 어떻게 생겼든 배열의 길이는 똑같아지기에 둘의 split()길이가 같으려면 각각의 배열에 찾는 알파벳 p, y의 개수가 같아야 할 수밖에 없다. 둘 다 없으면 0이 나올거고 이 경우에도 true가 반환되니 해답에 지장이 없다.

     

    [문자열 다루기 기본]

     

    https://programmers.co.kr/learn/courses/30/lessons/12918

    소요시간: 3시간 정도

     

    처음 작성 코드

    function solution(s) {
        return isNaN(s/1) ? false : true
    }

    s내 모든 내용이 숫자로만 되어있으면 저렇게 했을 때 자동으로 숫자로 변환된다.

    그렇지 않으면 NaN값이 뜨기에 isNaN() 함수로 NaN여부 확인!

    될줄 알았는데 안되는 케이스가 있었다...왤까??

    s = " "
    
    console.log(s/1) // NaN이 아닌 0이 나옴

    이런 경우 NaN이 나오지 않는다. 문자열 길이가 1 ~ 8 이라고만 했지 채워져 있다곤 안했다..ㅎ

    그럼 저 경우만 예외처리로 빼주면 되지 않나?

     

    s = "   "
    
    arr = [...s]
    answer = []
    let i = 0
    
    for (i ; i < arr.length ; i++) {
    
      if(arr[i] !== ' ') {
        answer[i] = answer[i] + arr[i]
      } else {
    
      }
    }
    console.log(answer/1) // 0

    => 놉! 위 외에도  s.replace(' ','')라던가 split(),이라던가 어떤 경우에도 공백을 없애기는 하지만 공백의 배열을 /1 하면 0이 나온다..

    다른 방법을 찾아보자!

     

    + Number() 도 써보았지만 안된다.

    다른 분의 말에 따르면 다음과 같다.

    "숫자로 변환하기 위해 보통 Number 함수를 사용하셨을텐데, Number 함수는 e도 숫자로 변환하여 지수로 계산합니다."

     

    오케이,, 다른 방법!

    function solution(s) {
    
        let arr = s.split('')
    let answer = []
    let i = 0
    
    for (i ; i < arr.length ; i++) {
    
      if(arr[i] !== ' ') {
        answer[i] = arr[i]
      } // 공백제거 조건, else는 별도로 필요없다.
      }
     return answer.length !==arr.length ? false :
     (s/1 + "") == s ? true : false // s라는 문자열을 숫자화 한 후 다시 문자화 하는 과정, s에 알파벳이 섞였다면
     // 숫자화할 때 NaN이라는 숫자 데이터가 됨 이를 다시 문자화 하면 s와 값이 같지 않음
        
    }

    이래도 안된다..

     

    그러다가 정말 어이없이 문제를 해결했다...

    function solution(s) {
    
        let arr = s.split('')  // 배열화, s = '123a' -> ['1', '2', '3', 'a']
    	let answer = [] 
    	let i = 0
    
    if (s.length == 4 || s.length == 6) { // 문자길이 체크
    
    for (i ; i < arr.length ; i++) { 
    
      if(arr[i] !== ' ') {
        answer[i] = arr[i] // 공백이 아닌 값만을 넣어줌, 공백제거
      }
      }
    	return answer.length !==arr.length ? false :
        // 공백 값이 들어있는 문자열이면 기존과 길이가 달라졌을 것 -> false
     (s/1 + "") == s ? true : false
     
     // s라는 문자열을 숫자화 한 후 다시 문자화 하는 과정, s에 알파벳이 섞였다면
     // 숫자화할 때 NaN이라는 숫자형 데이터가 됨, 이를 다시 문자화 하면 문자형 'NaN'이 됨 -> s와 값이 같지 않음
     
     
     // answer.length !==arr.length ? false : isNaN(s) ? false : true
     // 이 코드도 써보았지만 지수형태의 예외 때문에 테스트 11번에서 탈락,
     // 답안에 있는 거 보니 과거에 테스트케이스가 적을 땐 됐었나보다..
     
     
    } else {
      return false
    }
    
    }

    알고 보니 문제 조건에 있던 문자열의 길이 4 나 6이라는 것을 맞추어 주어야 했다..

    난 문자열의 길이가 어떻든 간에 잘 동작한다고 생각해서 처음엔 신경쓰지 않았는데,,

    생각해보면 저것도 

    '문자열 s 길이가 4 혹은 6'이고, 숫자로만 구성돼있는지 확.인.해.주.는 함수였으니까

    넣는 게 맞기는 했다.. 그래서 안됐었구나...

     

    + 여담이지만 다른 사람들도 isNaN 방식을 썼었다. 하지만 지수형식("1e22")을 넣으면 문제가 된다고 한다.

    지수: 아주 크거나 아주 작은 숫자는 '5E10'과 같이 지수형으로 표현한다고 한다.

     

    그외 정규식이나 every() 등 편의적인 방법 들이 있었지만

    인상깊은 코드가 하나 있었다.

     

    프로그래머스 답안

    function alpha_string46(s){
      var result = false
      // 함수를 완성하세요
      if(s > 999){
        if(s <10000){
          result = true
        }else if(s > 99999){
          if(s < 1000000){
            result = true
          }else{
            result = false
          }
        }
      }
    
      return result;
    }

    나와 같이 동적언어의 속성을 이용한 코드였고 심지어 매우 순수하게 if문과 for문만 활용한 지극히 단순한 코드였다.

    비록 숫자가 0000이나 0123처럼 시작할 경우 실패한다는 예외가 있긴 했지만 인상적이어서 남겨본다..

    s를 숫자와 비교하면 s라는 문자열을 숫자로 바꾸게 될 것이고 그 과정에서 알파벳이나 공백이 섞인 경우는 NaN이 되어버려 조건을 충족시키지 못할 것이다.

     

    프로그래머스 답안 2

    var regex = /^\d{6}$|^\d{4}$/;
      return regex.test(s);

     

    그 외 다른 사람의 답안

    https://velog.io/@dosanahnchangho/javascript-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%8B%A4%EB%A3%A8%EA%B8%B0-%EA%B8%B0%EB%B3%B8

    function solution(s) {
    	const len = s.length
    
    	if(len === 4 || len === 6){
    		return s.split("").every((c)=>!isNaN(c))
    	}
    
    	return false
    }

     

    이것들은 추후 차근차근 알아가보자.

     

     

    [완주하지 못한 선수]

     

     

    https://programmers.co.kr/learn/courses/30/lessons/42576

    소요시간: 1시간

     

    처음에 복잡하게 코드를 쓰다가

    그것을 버리고 좀 더 효율적인 방법이 없을까 생각했다.

    해당 문제는 값의 차이가 하나뿐이고 나머지 값들은 순서가 무질서한 채로 모두 들어있으니..

    ...어? 순서..? 생각해보니 sort()로 정렬하면 값들이 똑같이 나열되겠단 생각이 들었다.

    차이가 나는 부분만 어떻게 꺼낼지 고민했으나 답이 쉽사리 내려지지 않았고

    우선 문제풀이 공유의 시간이 있어 어쩔수 없이 구글링을 하게 되었다.

    그 결과

    function solution(participant, completion) {
    
    let i = 0
    
    participant.sort()
    completion.sort()
    
    for (i ; i < participant.length ; i++) {
    if (participant[i] !== completion[i]) {
    
      return participant[i]
    
    } else {
        }
    }
    }

    이와 같은 코드가 나올 수 있었다.

    실제로 차이가 나는 배열 값이 어디에 있건 그 앞까지는 모두 똑같을 것이고 다른 부분은 반복문을 돌리며 검사하다 마주치는 구간 하나뿐일 것이다. 그때 participant[i]를 구한다. completion을 쓰면 안되는 것은 그 배열의 값은 기본적으로 participant에 그대로 들어있기 때문이다. completion에 없는 값을 구하는 문제이니까 당연한 거다.

     

    다른 답안엔 무엇이 있을까?

     

    var solution=(_,$)=>_.find(_=>!$[_]--,$.map(_=>$[_]=($[_]|0)+1))

    이..이게 뭐노...

     

    모르는 것은 찾아보면서 공부하면 좋긴하지만

    지금 이걸 바로 뜯어볼 건 아닌거 같다..

    레벨 10이 레벨 100 사냥터에 가는 데에는 순서가 있다고 본다.

     

    var solution=(participant,completion)=>participant.find(name=>!completion[name]--,
    completion.map(name=>completion[name]=(completion[name]|0)+1))

    (원래 엔터를 치면 안되지만 지금은 코드를 곱씹어보는 시간이니 임의로 배치했다.)

    위 코드는 해당 코드를 댓글에서 풀어 쓴 것으로 추후 이해에 더 도움될 것 같아 들고와 보았다.

     

    음.. 이 역시 레벨 50정도 되는 것 같다...

     

     

    그나마 현재 수준에서 이해할만한 정도라면

    function solution(participant, completion) {
        const map = new Map(); // map 함수를 객체화해준다.
    
        for(let i = 0; i < participant.length; i++) {
            let a = participant[i], 
                b = completion[i]; 변수에 각 값을 대입
    
    		// map.set(key, value)와 같은 형태로 사용, map 객체에 해당 키 : 밸류 를 저장해준다.
            // a, 즉 participant의 첫번째 값.  키: a | 밸류: (map.get(a) || 0) + 1 설정
            // map.get(a), 즉 a라는 키의 밸류를 불러오는 것, 여기서 a는 처음 추가 되는 것으로 
            // map.get(a)는 false 반환할 것, false값은 0 -> 0 || 0 이면 0이 출력, 0+1 == 1
            // 결과적으로 키: a 밸류: 1 이 저장됨
            map.set(a, (map.get(a) || 0) + 1)
            
            //그런데 만약 map에 a가 미리 추가되어 있는 상황 (동명이인으로 인해) 이라면
            map.get(a) || 0 이라는 조건 구문에서 map.get(a)가 true가 나올 것
            True 또는 False 라면 True 값인 1이 출력되고 1+1은 2가됨
            
            map.set(b, (map.get(b) || 0) - 1);
           //a와 같은 원리로 b에도 키 : 밸류를 만드는 도중
            //b의 값이 완주자라면 참가자 리스트에도 이름이 있고 완주자 리스트에도 그 이름 값(b의 값)이 있음
            // map.get(a) == map.get(b) == 1 임
            // b 세팅에서 -> 1 - 1 = 0이 됨
            // 완주를 하지 못한 경우에 참가자 리스트 a 에는 값이 있지만 완주리스트에는 없어서
            // b에는 변화가 일어나지 않음 -> map.get이 되지 않아 -1이 작동하지 않음
            // -> 해당 특정 이름에 대한 밸류는 1로써 유지됨
            map.get(b)는 false, false || 0 -> 0이 출력되며 0 - 1은 -1이 됨
            // b가 완주자 리스트에 없지만 a와 동명이인인 경우에도 a는 위 식들에 따라 2가나오고 b는 0 - 1이 나옴
            //map.set이 둘다 호출되니까 2 - 1 을 하게 되면 1이 됨
            
        }
    
        for(let [k, v] of map) { // 이제 맵에서 키와 밸류 들을 루프로 풀어서 나열하게 되는데
        //이때 v, 즉 밸류를 살펴보면 다음과 같음
        //참가자에 있고 완주 = 0, 참가자에 있지만 완주 실패 = 1, 참가자에 동명이인이 있음, 근데 본인은 실패 = 1
            if(v > 0) return k; // 따라서 밸류가 0보다 큰 경우의 키값(사람이름)을 출력하면
            // 동명 이인이든 아니든 완주 실패자의 이름을 출력하게 되는 것
        }
    
        return 'nothing';
    }

    map.set이나 map.get은 쉽게 말하면

    map.set(키, 밸류) - map 객체에 키 : 밸류 형태의 요소를 넣어주는 것

    map.get(키) - map 객체로부터 키 : 밸류 형태의 요소를 가져오는 것

     

     

    공식 문서에 따르면 for ... of 구문은 다음과 같다.

    for (variable of iterable) {
      statement
    }

     

     

    iterable이라고 해서 반복처리 가능한 객체 정도로 보면 될 것 같다. 문자열, 인자, map, 배열, set 등이 들어갈 수 있는 자리다.

    해당 반복가능 객체에서 변수를 꺼내오는 루프를 만들어주는 것이 for ... of 기능이다.

     

    Mozilla 재단의 예시를 첨부하자면 다음과 같다.

    const array1 = ['a', 'b', 'c'];
    
    for (const element of array1) {
      console.log(element);
    }
    
    // expected output: "a"
    // expected output: "b"
    // expected output: "c"

    이처럼 값을 하나씩 꺼내와 출력하는 루프를 만들어준다고 보면 되겠다.

     

     

    위 코드를 이해하기도 정말 힘들었지만 하나하나 뜯어보니 map으로 키 값을 설정하거나 가져오는 것에 대해서 좀 더 이해가 된다.

     

     

    [서울에서 김서방 찾기]

    특정 문자열 'Kim'의 위치를 찾는 문제

    https://programmers.co.kr/learn/courses/30/lessons/12919

    소요시간: 23분

     

    처음 쓴 코드

    function solution(seoul) {
        let i = 0
    
    for (i ; i < seoul.length ; i++) {
    
      if (seoul[i] == 'Kim') {
        
        return `김서방은 ${i}에 있다` // 자바스크립트의 백틱기능을 이용했다.
        // 제이쿼리를 쓸 때와 같은 형식으로 사용해준다.
      }
    }
    }

    다른 기능들을 이용해 하는 법은 없을까?

    문자열이 짧아서 지금은 괜찮지만 길어질 경우 처리 효율이 떨어질 수 있다.

     

    그래서 map arrow function이나 위에 나온 map 객체 생성을 해보려 했으나

    이번 문제에서는 크게 필요치 않았다.

     

    그래서 메서드를 이용해 간단히 구현해본 코드

     

    function solution(seoul) {
    
    return `김서방은 ${seoul.indexOf("Kim")}에 있다`
    }

    끝.. 깰꼼..ㅎㅎㅎ

    indexOf(searchElement, fromIndex)

    indexOf는 배열 내 특정 값이 어디에 있는지 찾아서 인덱스를 반환해주는 메서드다.

    만약 배열 내 값이 없다면 -1을 반환한다.

    fromIndex는 필수 값이 아니며 넣어줄 경우 그 인덱스에서 부터 찾기 시작한다.

    이 메서드는 searchElement와 완전히(===) 똑같은 값을 찾으며 순서대로 찾다가 가장 먼저 찾게 되는 값의 인덱스만 출력해주고 종료된다. 이 문제에서는 Kim이 한명 뿐이니 문제가 없다.

     

    프로그래머스 모범답안이나 기타 답안들도 대부분 위 두 가지 케이스로 해결했기에 이 정도면 우선 충분한 리팩토링으로 보인다.

     

     

    [수박수박수박수박수박수?]

     

     

    https://programmers.co.kr/learn/courses/30/lessons/12922

    소요시간: 14분 2초

     

    처음 쓴 코드

    function solution(n) {
        
        let i = 0
    let answer = ""
    
    for (i ; i < n ; i++) {
    
    if (i%2 == 0) { // i는 1이 아니라 0으로 시작하기에 시작점을 맞추고자 i가 0 혹은 짝수일 경우 '수'
    answer = answer + "수"
    }
      if (i%2 !== 0) { // 홀수일 경우 '박'
    
    answer = answer + "박"
      }
    }   
        return answer;
    }

    아주 깔끔하고 단순한 코드다.

     

    좀 더 효율적이거나 다르게 짜볼 순 없을까?

     

    리팩토링 소요시간: 34분

    function solution(n) {
    
    let arr = Array(n).fill()
    
    return arr.map((v, i) => i%2 == 0? arr[i] = "수" : arr[i] = "박").join("")
    
    }
    

     

    짜잔! 전에 잤던 코드와 map()함수를 좀 활용해보았다.

    n 갯수에 맞게 공간이 배정된 배열에 map으로 값을 배정하고 배열들을 빈 공간없이 합치면 원하는 결과가 나온다 :)

     

    +

    Array(n).fill().map((v, i) => i%2 == 0? Array(n).fill()[i] = "수" : Array(n).fill()[i] = "박").join("")

    참고로 이렇게 한 줄로 하는 것도 된다!

    오오..신기해

     

    다른 모범 답안들을 보니 repeat()이나 slice()를 쓰곤 했다.

    먼저 repeat()을 보자

     

    const waterMelon = n => {
        return '수박'.repeat(n/2) + (n%2 === 1 ? '수' : '');
        
        // n%2 === 1부분에서 처음 글자를 고르고 1 <= n/2 부터는 repeat()이 작동하기 시작
    }

    n => {}

    라고 한 것은

    function (n) {} 와 같다. 화살표 함수이다.

     

    다음으로 slice()를 보자

    const waterMelon = n => "수박".repeat(n).slice(0,n);

    Good.

     

     

     

    ▶ 느낀 점

     

    알고리즘 문제를 풀면서 알게되는 새로운 개념은 여전히 많기에

    한 문제를 풀 때 마다 풀고나서 리팩토링, 관련 개념 조사, 모범답안 구조 이해 등 시간이 꽤나 걸린다.

    게다가 푸는 거 자체도 엉뚱한 방향으로 사고하다보면 꽤 시간이 걸리기도 한다. 하지만 그 행동들 덕분에 나는 오늘도

    자기 주도적 학습을 했다. 고생해서 배웠기에 더 기억에 남고 더 잘 이해할 수 있으리라 생각한다.

    오늘은 이만 자야지 (새벽2시 ㅎㅎ..)

     

    내일부터는 cs스터디도 진행되고 알고리즘 모의고사도 있다..!

    신난다 ㅎㅎㅎ

    아 자바스크립트 기초문법 강의는 아직 못봤는데 긴 거 아니니까 꼭 보도록 해야겠다.

    또 모르는 건 모던 자바스크립트 영문판, 한글판도 봐야겠고 말이다 :)

     

     

    ▶ 공부 시 참고 링크들

     

    https://www.delftstack.com/ko/howto/javascript/convert-string-to-array-javascript/

     

    JavaScript에서 문자열을 배열로 변환

    이 튜토리얼에서는 쉼표가있는 문자열을 JavaScript 배열로 변환하는 방법을 소개합니다.

    www.delftstack.com

     

    https://velog.io/@mnz/JavaScript-number%EB%9E%80

     

    [JavaScript] Number란?

    JavaScript Number에 대해 정리한 글입니다.

    velog.io

     

    https://wooder2050.medium.com/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-javascript-2ecb52443e8d

     

    [알고리즘] 완주하지 못한 선수-JavaScript

    Algorithm Problem with JavaScript — 1 day

    wooder2050.medium.com

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set

     

    Map.prototype.set() - JavaScript | MDN

    The set() method adds or updates an element with a specified key and a value to a Map object.

    developer.mozilla.org

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

     

    for...of - JavaScript | MDN

    The for...of statement creates a loop iterating over iterable objects, including: built-in String, Array, array-like objects (e.g., arguments or NodeList), TypedArray, Map, Set, and user-defined iterables. It invokes a custom iteration hook with statement

    developer.mozilla.org

     

    https://hianna.tistory.com/404

     

    [Javascript] 배열 특정 값 위치(index) 찾기 - indexOf(), lastIndexOf()

    배열에서 특정 값의 위치 index를 찾는 방법을 소개합니다. Javascript에서 배열에서 특정 값의 위치를 찾는 방법은 다양합니다. 이번에는 먼저, indexOf() 함수와 lastIndexOf() 함수를 사용하여 배열에서

    hianna.tistory.com

     

    '항해 99' 카테고리의 다른 글

    항해 99 5기 TIL_10  (0) 2022.01.19
    항해 99 5기 TIL_9  (0) 2022.01.18
    항해 99 5기 WIL_1  (0) 2022.01.16
    항해 99 5기 TIL_7  (0) 2022.01.16
    항해 99 5기 TIL_6  (0) 2022.01.15
Designed by Tistory.