-
항해 99 5기 TIL_7항해 99 2022. 1. 16. 15:10
▶ Today I Learned
<WIL작성하며 회고>
일주일 간 배웠던 내용들을 짚어보고 다시 한 번 정리하였다.
상세한 내용이 궁금하다면 WIL_1을 보면 된다.
<알고리즘 기초>
[x만큼 간격이 있는 n개의 숫자]
https://programmers.co.kr/learn/courses/30/lessons/12954
소요시간: 12분 53초
처음 작성한 코드
function solution(x, n) { let i = 0; let adding = 0; var answer = [] for (i; i < n ; i++) { adding = adding + x answer[i] = adding } return (answer) }
매우 많이 쓰이는 패턴이고 깔끔하다.
더 효율적인 방법은 없을까??
reduce 구문이 바로 생각났다.
그런데 지난번 reduce구문을 정리하곳 사용도 해보았지만 뭔가 좀 찜찜했다.
그러던 중 Mozilla 재단의 Syntax를 다시보니 그제서야 이해가 되었다.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
reduce((previousValue, currentValue, currentIndex, array) => { /* ... */ }, initialValue)
initialValue는 좌측이 아닌 우측에 저렇게 쓰는 것이었다. 이제 완전히 이해가 되었다 ㅎㅎ
cf) iterator (반복자)
전에 구글링 하던 중 [a, a+1, ... a+n]과 같이 작성하면 저절로 저런 형식의 배열이 만들어진다는 것을 본 적이 있는 거같아 시도해보니 다음과 같은 에러가 발생했다.
iterable? 처리를 반복할 수 있는 이라는 뜻이었다.
찾아보니 자바스크립트엔 iterator라는 게 있었다.
아직 완벽히 숙지 하진 못했지만 찾다보니 다음과 같은 점을 발견할 수 있었다.
문자열 앞에 ...을 붙이니 문자들이 하나씩 떨어져 출력되는 것! 앞으로 문자를 분리할 때는 이 방법도 활용해보아야겠다! ㅎㅎ
문득 쓰다가 들었던 생각은 reduce구문을 쓰려면 answer라는 배열에 값이 모두 채워져있는 경우에 가능하겠다는 것이었다.
그래야 배열안에서 특정 인덱스를 지정해 거기서 부터 값을 빼오고 연산을 시작해 결과 값을 출력할 테니까..
더 붙잡고 싶지만 우선 나에게는 주어진 시간이 있기에 모범답안을 확인해보았다.
프로그래머스 모범답안
function solution(x, n) { return Array(n).fill(x).map((v, i) => (i + 1) * v) }
결론: 길이가 n인 배열에 x라는 값을 모조리 채운 다음 인덱스 0 값은 그대로 놔두고 인덱스를 1씩 증가시키며 각 요소값인 x를 곱해서 그것을 순서대로 집어넣어 주는 것이다.
이것도 가히 예술적이다..
+ 짜잔! 야생의 map이라는 기능이 튀어나왔다..!
map은 특정 배열의 인자마다 특정 조건을 적용한 새로운 배열을 만들어내는 기능이다.
Arrow function을 사용한 생김새는 이렇다.
map((element, index, array) => { /* ... */ })
여기서 위 예시처럼 index와 array는 없을 수도 있다.
그렇담 fill()은 무엇인가??
fill(value, start, end)
특정 value를 주게 되면 그 값으로 배열을 다 채우는 기능이다. start와 end 인덱스는 optional이며
start default는 0, end default는 arr.length이다. end index의 전까지 채워주기 때문에 배열의 길이가 end여도 default값으로써는 문제가 없다.
[2016년] 2016년 a월 b일 이라는 날짜 값을 입력하면 그 날짜의 요일을 출력하는 함수
https://programmers.co.kr/learn/courses/30/lessons/12901
소요시간: 49분 1초
날짜 데이터를 불러오는 기능이 필요할 것 같다
모질라 재단의 공식 문서를 보던 중 이것을 발견했다.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDay
날짜.getDay()라는 형식으로 함수를 쓰면 특정한 날짜의 요일을 0부터 6까지의 숫자로 출력해준다.
왠지 이걸 쓰면 될 것 같다.
여기서 잠깐, 분명 월에는 12를 출력했는데 왜 출력날짜에선 한달 뒤가 나올까..?
그건 Mozilla 재단 공식 페이지에서 다음과 같이 확인할 수 있다.
new Date(year, monthIndex, day)
monthIndex
Integer value representing the month, beginning with 0 for January to 11 for December. If a value greater than 11 is passed in, then those months will be added to the date; for example, new Date(1990, 12, 1) will return January 1st, 1991
즉, 0을 입력하면 1월, 11을 입력하면 12월이 되는 것이다..월을 11을 초과해 입력하게 되면 초과한 개월 수 만큼 출력 날짜도 뒤로 밀리는 것이다.
그래서 제출한 코드
function solution(a, b) { let theDay = new Date(2016,a-1,b) // 개월 수를 맞춰주기 위해 a-1을 한다. var weekdays = new Array('SUN','MON','TUE','WED','THU','FRI','SAT') // 차례대로 0부터 Sun이 출력된다. return weekdays[theDay.getDay()] // getDay()를 써서 0 ~ 6 중 요일에 따른 수를 출력하고 // 이를 배열의 인덱스로 삼는다. }
여기서 이번엔 Array 생성을 생성자를 사용해서 해보았다.
사실 weekdays = ['SUN','MON','TUE','WED','THU','FRI','SAT']로 해도 된다.
하지만 생성자 생성방식도 있다고 하여 써보고 싶었다.
여기서 생성자란 무엇이며 그냥 배열을 만드는 것과 무슨 차이가 있을까??
https://ko.javascript.info/constructor-new
그것은 상단 링크 내 문서를 확인하면 좀 더 쉽게 이해할 수 있다.
우리가 흔히 쓰는 객체 리터럴 방식 (ex: var arr = ['1', '2']) 보다 훨씬 이해하기 쉽고 간결한 코드를 쓸 수 있다는 것이다. 둘다 기능상 차이는 없으나 객체 리터럴의 경우 괄호 안에 쉼표를 찍고 객체를 하나씩 추가해주어야 한다.
객체 리터럴을 통한 객체 생성
let counting = { number = 1 // 객체를 만들어 주려면 number = 2, number = 3 처럼 계속 일일이 추가해주어야 한다. };
생성자를 통한 객체 생성
function Count(number) { this.number = number; } let counting = new Count(1); console.log(counting.number) // 1 // 원하는 객체가 있으면 그때마다 number자리에 다른 걸 집어넣으면 된다. // 재사용 할 수 있는 객체 생성 코드이다.
사실 이번 알고리즘 문제에서는 객체 리터럴을 썼어도 차이는 없다고 본다.
다만 호기심이 생겨 이번 기회를 통해 생성자를 공부해보았다.
아직 완전히 파악한 것은 아니며 어떤 분야에 어떻게 쓸지는 잘 모르겠지만 (왠지 로그인 부분에 쓸수도 있겠단 생각이 든다.)
우선 이렇게 어느 정도 알아두고 추후 맞닥뜨려야 할 때 좀 더 공부해볼 생각이다.
프로그래머스 모범 답안 (1)
function getDayName(a,b){ var date = new Date(2016, (a - 1), b); return date.toString().slice(0, 3).toUpperCase();
쉽게 말하자면 나와 같이 Date 메서드를 쓰고 이를 toString()으로 나타내면 요일이 맨 앞에 Tue와 같이 나온다. 그러면 slice()를 활용해 요일만 잘라내고 이를 대문자의 문자열로 바꾸는 toUpperCase() 메서드를 이용해 반환해준다.
음... Date() 메서드를 쓰지 않고 하는 법은 없을까..?
프로그래머스 모범 답안 (2)
function getDayName(a,b){ var dayList = ['FRI','SAT','SUN','MON','TUE','WED','THU']; var monthArr = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; var daySum; if(a < 2) { daySum = b - 1; } else { daySum = monthArr.slice(0, a - 1).reduce((a, b) => a + b) + b - 1; } return dayList[daySum % 7]; }
2016년 1월 1일이 금요일이었다는 점을 반영해서 직접 짜신 코드이다. dayList가 금요일로 시작한다.
1월인 경우 -1을 해준 이유는 추후 daySum을 7로 나누어 남는 나머지를 dayList의 인덱스로 쓰는데
인덱스의 시작이 1이 아닌 0 이기 때문에 오차를 없애려고 한 것이다.
뒤의 reduce 부분은 다음의 코드를 보면 쉽게 이해할 수 있다.
const array1 = [1, 2, 3, 4]; const reducer = (previousValue, currentValue) => previousValue + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // expected output: 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // expected output: 15
출처: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
위에서 알 수 있듯
reduce((a, b) => a + b)는 slice로 추출한 배열 값들의 총합이 되고 + b - 1은 날짜의 기준을 맞추기 위해 추가로 삽입한 것이다.
ex) 1월 1일의 경우 금요일 인데 dayList[0]이어야 하나 1을 빼주지 않으면 dayList[1]이 되어버리기 때문이다.
그렇다면 그 뒤로는 몇을 더하여도 daySum에서 7을 기준으로 나눠버리니 요일은 순서대로 알맞게 간다. 요일은 윤년이든 아니든 항상
7일 단위로만 끊어지기 때문이다.
[부족한 금액 계산하기]
https://programmers.co.kr/learn/courses/30/lessons/82612
소요시간: 46분 36초
위의 [x만큼 간격이 있는 n개의 숫자]의 모범답안 코드를 활용해 내가 제출한 코드
function solution(price, money, count) { let priceArr = new Array(count) let total = priceArr.fill(price).map((price, i) => price*(i+1)).reduce((acc, cur) => acc + cur) //배열 내 price라는 값 똑같이 넣어줌, 그리고 인덱스 1부터 price에 횟수를 곱한 값을 차례대로 넣어줌 // 그 다음 배열의 총합을 구함 if (money < total) { // 돈이 모자라면 모자란 금액 반환 return (total-money) } else { // 모자라지 않으면 0 반환 return (0) } }
그러나 기본 문법을 활용해서 코드를 짠다면?
let price = 3 let money =20 let count = 4 let i = 0 let total = 0 for (i ; i < count ; i++) { total = total + price*(i+1) } if (money < total) { console.log (total-money) } else { console.log (0) }
이런 식의 코드를 짤 수도 있겠다. 개인적으로 이게 더 짜기 쉬웠다.
프로그래머스 모범답안
function solution(price, money, count) { const tmp = price * count * (count + 1) / 2 - money; return tmp > 0 ? tmp : 0; }
아..맞다 가우스 공식이 있었지... 총합 구할 땐 되도록 가우스 공식 먼저 써봐야겠다..
물론 숫자간 간격이 일정하다면 말이다..
[나누어 떨어지는 숫자 배열]
https://programmers.co.kr/learn/courses/30/lessons/12910
소요시간: 1시간 31분
function solution(arr, divisor) { let i = 0; let answer = [] for (i ; i < arr.length ; i++) { if (arr[i]%divisor == 0) { answer.push(arr[i]) // 공백 제거를 위해 조건에 해당될 때만 push로 값 넣어줌 } else { // 아무것도 안함 } } // 반복문 끝, 배열 생성됨 if (answer.length == 0) { // 배열에 값이 있는지 없는지 검사 return answer = [-1] // 없으면 -1 반환 } else { return answer.sort((a, b) => a - b) // 오름차순으로 정렬 } }
sort()라는 함수는 오름차순으로 배열의 값을 정렬하여 새로운 배열을 만들어주는 함수이다.
여기서 주의할 점 2가지
1) sort()를 하면 기존의 배열도 바꿔버린다. 즉, 기존의 배열은 없어지고 '기존의 배열에 sort()를 적용한 배열'만 남게된다.
2) sort()는 기본적으로 알파벳(UTF-16)을 기준으로 오름차순을 해준다. 따라서 숫자 배열에 그대로 적용하면 제대로 정렬되지 않는다.
그래서 여기서는 compareFunction (a, b)를 사용한다.
sort의 ()안에는
sort(compareFunction(a, b){
return 값
})와 같은 함수가 있다고 보면 된다.
여기서 return되는 값이 양수일 경우 b가 앞으로 오고 0일 경우 순서유지, 음수일 경우 a가 앞으로 온다.
따라서 return a-b를 하면 오름차순이 적용된다.
기본적으로 배열 내 두 값을 비교하도록 되어있어서 배열의 길이나 값에 상관없이 알아서 비교된다.
위 식을 화살표 함수로 나타내면 sort((a, b) => a - b) 가 된다.
이는 다음과 같다.
sort (function(a, b) {
return a-b;
})
사실 sort()같은 기능을 쓰지 않고 조건문같은 기본 기능만으로 이것을 표현할 수 없을까하고 계속 고민했다.
하지만 찾지 못해서 우선 사용했고 다른 사람의 풀이를 확인해보았다.
프로그래머스 몇몇 모범 답안들도 전부 오름차순에는 sort를 쓴 것으로 보아 꽤 많이 쓰는 것 같다.
앞으로 차순 정렬이 필요하면 우선 sort() 기능을 유용하게 사용해보아야 겠다.
복잡한 기능도 아니라서 실무에서도 나름 요긴하게 쓰이지 않을까 생각한다.
+ 그러던 중 sort()를 쓰지 않은 코드를 발견했다.
https://programmers.co.kr/learn/courses/30/lessons/12910/solution_groups?language=javascript
function solution(arr, divisor) { var answer = []; for (var i = 0; i < arr.length; i++) { if (arr[i] % divisor === 0) { answer.push(arr[i]); } } for (var j = 0; j < answer.length; j++) { if (answer[j] > answer[j + 1]) { var temp = answer[j]; answer[j] = answer[j + 1]; answer[j + 1] = temp; j = -1; } } if (answer.length === 0) { answer.push(-1); } return answer; }
사실 나도 앞과 뒤의 순서를 바꾸는 것을 생각하고 구현하려 했다.
하지만 i라는 인자 하나만을 쓰고있어 할 수 없었는데
저분처럼 j라는 인자를 하나 더 만들면 따로 분리해서 볼 수 있었다.. (인자를 하나로 쓸 경우 i에 0을 넣게되면 i+1이라는 식을 쓸 수 없다. 시작할 때 answer라는 배열에는 answer[0]만 있지 answer[1]는 없기 때문이다. 그렇다고 i-1과 비교를 하면 answer[-1]이 생겨나서 안된다.)
게다가 이미 순서대로 배정된 배열 값을 어떻게 앞 뒤로 바꿀까 생각했었다.
저분처럼 answer[j] = answer[j + 1];하면 되는 것이었다. temp라는 값에 기존 answer[j] 값을 빼두면 값이 유실될 일도 없었다.
앞으로 정렬을 위한 코드를 짤 때는 저 구조를 다시 한 번 되새겨 보아야겠다..
[내적]
소요시간: 8분 9초
https://programmers.co.kr/learn/courses/30/lessons/70128
function solution(a, b) { let i = 0 let c = [] for (i ; i < a.length ; i++) { c[i] = a[i]*b[i] } let answer = c.reduce((a, b) => a + b) return answer }
이것을 다른 방법으로 풀어본 결과
function solution(a, b) { return Array(a.length).fill(1).map((v,i) => (a[i]*b[i]*v)).reduce((a, b) => a + b) }
프로그래머스 모범 답안에서 배웠던 기능들을 모두 활용해봤다 :)
두 코드 모두 정상적으로 실행되었다. ㅎㅎ 뿌듯쓰
▶ 느낀 점
여전히 알고리즘을 풀어내는 데는 꽤 많은 시간이 소요되고 있다. 그래도 간단한 패턴 같은 것들은 보자마자 어느정도 구상이 되고 있다. 좀 더 다양한 패턴을 연습하고 반복하면 확실히 빨라질 것 같다. 지금도 반복문으로 값을 하나하나 출력하는 패턴은 자동으로 나오고 있다.
자바스크립트의 문법도 조금씩 익숙해지고 있다. 이대로 꾸준히 성장하자.
▶ 공부 시 참고 링크들
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
https://kosaf04pyh.tistory.com/21
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
'항해 99' 카테고리의 다른 글
항해 99 5기 TIL_8 (0) 2022.01.18 항해 99 5기 WIL_1 (0) 2022.01.16 항해 99 5기 TIL_6 (0) 2022.01.15 항해 99 5기 TIL_5 (0) 2022.01.14 항해 99 5기 TIL_4 (0) 2022.01.14