sungyup's.

Web_Miscellaneous / Algorithm(JS) / 4.3 프로그래머스 Lv.0 #21~30

4.3프로그래머스 Lv.0 #21~30

프로그래머스 Lv.0 21~30번

개요

기초 트레이닝 문제(Lv.0) 21 ~ 30번 문제 풀이 중 배울 점이 있는 풀이들에서 배운 개념들을 내 것으로 만들고자 정리해본다.

22. 등차수열의 특정한 항만 더하기

문자열 섞기는 딱 보고 reduce를 쓸 수 있는 상황이란걸 알았지만 익숙하지 않아 그냥 단순하게 풀었다. 이번 기회에 reduce도 정리해본다.

javascript
function solution(a, d, included) { const array = []; included.forEach((val, idx)=> { array.push(val * (a + idx *d)) }) let answer = 0; for(let n of array){ answer += n; } return answer; }

22-1. 물론 reduce 활용이 가능하다.

boolean 값의 배열인 included의 원소 이름들을 flag라고 붙인게 인상 깊었다.

javascript
function solution(a, d, included) { return included.reduce((acc, flag, i) => { return flag ? acc + a + d * i : acc }, 0) }

23.주사위 게임 2

주사위 게임 2는 세 가지 변수를 서로 비교하고, 어떻게 가독성 좋고 간편하게 원하는 값을 반환할 수 있는지 묻는 문제라고 생각한다.(구현은 누구나 할 수 있으니 코드의 질이 중요한 케이스가 아닐까)

나는 이렇게 풀었다:

javascript
function solution(a, b, c) { let allDiff = a + b + c; const twoSame = allDiff * (a ** 2 + b ** 2 + c ** 2) const threeSame = twoSame * (a **3 + b ** 3 + c ** 3) if(a === b && a === c) return threeSame; if(a === b && a !== c) return twoSame; if(a !== b && a === c) return twoSame; if(a !== b && b === c) return twoSame; return allDiff; }

이 문제는 풀면서도 다른 사람들의 풀이가 기다려졌다.

23-1. Set을 활용

중복된 데이터는 저장하지 않는 Set을 활용하는 방법이다. 이 방식은 확장성 측면에서 아주 좋다. 예를 들어, 주사위 4개를 던진다고 하면 즉각적으로 늘릴 수 있다.

javascript
const solution = (a, b, c) => { const set = new Set([a, b, c]); switch (set.size) { case 1: return calculate([a, b, c], 3); case 2: return calculate([a, b, c], 2); case 3: return calculate([a, b, c]); } }; const calculate = (inc, n=1) => { const [a, b, c] = inc; let result = 1; for (let i = 1; i <= n; i++) { result *= Math.pow(a, i) + Math.pow(b, i) + Math.pow(c, i) } return result; };

개인적으로 calculate 함수는 좀 오버킬이라고 생각한다. 아래와 같이 구현하는게 덜 아름다워도 낫다고 생각하고, 이후 확장이 된다면 바꾸는게 어떨까 싶다.

javascript
function solution(a, b, c) { const set = new Set([a, b, c]); const sum = a + b + c; const squareSum = a **2 + b**2 + c**2; const cubeSum = a**3 + b**3 + c**3; switch(set.size){ case 1: return sum * squareSum * cubeSum; case 2: return sum * squareSum; case 3: return sum; } }

24. 원소들의 곱과 합

원소들의 곱과 합 역시 문제 풀기 자체는 특별히 어렵지 않으나 얼마나 가독성 좋게 잘 쓰냐가 문제인것 같다.

javascript
function solution(num_list) { let a = 1; let b = 0; num_list.forEach(val => a *= val) num_list.forEach(val => b += val) return a > b ** 2 ? 0: 1; }

24-1. for...of문

어차피 num_list는 같으므로, 한번의 for...of문으로 두 개의 합을 모두 구할 수 있다. 이 답에서 또 인상깊었던 것은 역시 변수 명이 직관적이라는 것. accMul, accSum은 아주 좋은 이름이라 생각한다.

javascript
function solution(num_list) { let accMul = 1 let accSum = 0 for (const num of num_list) { accMul *= num accSum += num } return accMul < accSum ** 2 ? 1 : 0 }

24-2. 또또또 reduce

reduce는 이런 상황에서도 사용 가능하다. 특히, initialValue를 포함할 수 있기에 곱에서는 1을, 합에서는 0을 쓸 수 있고 이게 let a = 0; let b = 1;과 같은 표현보다 훨씬 이해하기 쉽다고 생각한다.

javascript
function solution(num_list) { const mul = num_list.reduce((a, c) => {return a * c}, 1); const sum = num_list.reduce((a, c) => {return a + c}, 0); return (mul < sum * sum) ? 1 : 0; }

25. 이어 붙인 수

이어 붙인 수 문제인데, 나름 for...of가 바로 생각나서 썼다.

javascript
function solution(num_list) { let concatOdd = "", concatEven = ""; for(let n of num_list){ n % 2 === 0 ? concatEven += n : concatOdd += n } return Number(concatEven) + Number(concatOdd); }

25-1. 여기도 reduce

reduceacc 매개변수를 destructuring한게 인상적이다.

javascript
function solution(num_list) { const { odds, evens } = num_list.reduce(({ odds, evens }, num) => { if (num % 2 === 0) { evens.push(num) } else { odds.push(num) } return { odds, evens } }, { odds: [], evens: [] }) return Number(odds.join('')) + Number(evens.join('')) }

26. 마지막 두 원소

마지막 두 원소 문제는 어려울게 없지만, 개인적으로 arr.length -2라는 표현을 쓴게 좀 꺼림칙했다. 이렇게 풀었다:

javascript
function solution(num_list) { const last = num_list[num_list.length -1]; const subLast = num_list[num_list.length -2]; const newLast = last > subLast ? last-subLast : last * 2 num_list.push(newLast) return num_list; }

26-1. spread operator와 deconstructing

Destructuring을 아주 잘 쓴 풀이다. arr.reverse() 메소드는 덤. destructuring을 하면 맨 앞의 두 요소만 빼는 것도 가능하다는 점을 상기시켜주는 풀이다.

javascript
function solution(num_list) { const [a, b] = [...num_list].reverse(); return [...num_list, a > b ? (a-b):a*2]; }

26-2. slice의 반환값

slice 메소드를 잘 이해하고 있다면 아래와 같은 풀이도 가능하다.

javascript
function solution(num_list) { const [sec, last] = num_list.slice(-2); last > sec ? num_list.push(last - sec) : num_list.push(last * 2); return num_list; }

27. 수 조작하기 1

드디어 reduce를 써서 풀었다. 물론 좀 어설프다...

javascript
const controlValue = { 'w': 1, 's': -1, 'd': 10, 'a': -10 } function solution(n, control) { const array = control.split(""); const answer = array.reduce((acc, cur) => { acc += controlValue[cur]; return acc; }, n) return answer; }

27-1. 더 멋지게

사실 이런 식으로 하고 싶었던 것이다.

javascript
const operations = { w: (n) => n + 1, s: (n) => n - 1, d: (n) => n + 10, a: (n) => n - 10, }; function solution(n, control) { return [...control].reduce((prev, op) => operations[op](prev), n); }

27-2. switch문 활용

switch문을 활용할 수도 있다.

javascript
function solution(n, control) { for(let i = 0 ; i < control.length ; i++){ switch(control[i]) { case "w" : n++; break; case "s" : n--; break; case "d" : n+=10; break; case "a" : n -=10; break; } } return n; }

27-3. forEach 활용

javascript
function solution(n, control){ let answer = n; const o = { "w": 1, "s": -1, "d": 10, "a": -10 } control.split("".forEach(e => answer += o[e])); return answer; }

28. 수 조작하기 2

27번 문제의 역산이라고 할 수 있는 문제다. 나는 이렇게 풀었다:

javascript
function solution(numLog) { const array = []; const key = { "1": "w", "-1": "s", "10": "d", "-10": "a", } for(let i = 1; i < numLog.length; i++){ array.push(numLog[i] - numLog[i - 1]); } return array.map(value => key[String(value)]).reduce((acc, cur) => { acc += cur; return acc; }, "") }

28-1. slice 활용하기

맨 처음 0은 샤실상 쓸데없기에 잘라버리고 시작하는 방식이다.

javascript
function solution(numLog) { const convert = { '1': 'w', '-1': 's', '10': 'd', '-10': 'a' }; return numLog.slice(1).map((v, i) => { return convert[v - numLog[i]] }).join('') }

28-2. reduce를 다르게 쓰기

나는 numLog의 원소들의 차이로 다른 array를 만들어 풀었지만, numLog 자체에 reduce를 쓰는 풀이도 가능하다.

javascript
const differences = { '1': 'w', '-1': 's', '10': 'd', '-10': 'a', }; function solution(numLog) { return numLog.reduce( (result, curr, i) => (i === 0 ? result : result + differences[curr - numLog[i - 1]]), '', ); }

29. 수열과 구간 쿼리 3

수열과 구간 쿼리 3은 array 복사, 정확히는 요소간 스왑 기법에 관한 문제라고 생각한다. 나는 temp라는 변수를 두고 거기에 옮겨뒀다가 해당 요소가 바뀌면 temp에서 꺼내와 스왑하는 방식을 썼다:

javascript
const queryOperation = (arr, query) => { let temp = arr[query[0]]; arr[query[0]] = arr[query[1]]; arr[query[1]] = temp; return arr; } function solution(arr, queries) { let answer; for(let i =0; i<queries.length;i++){ answer = queryOperation(arr, queries[i]) } return answer; }

29-1. forEach를 통한 해답

하지만 반드시 temp를 쓸 필요는 없다. 아래와 같이 바로 queries 안을 순회하며 바꾸는 방식이 가능하다.

javascript
function solution(arr, queries) { queries.forEach(([a,b]) => { [arr[a],arr[b]] = [arr[b],arr[a]]; }) return arr; }

30. 수열과 구간 쿼리 2

이 구간에선 reduce에 맛들린 나머지 계속 reduce를 쓰고 있다. 29번을 풀 때 썼던것처럼 queryOperation이라는 함수를 만들고 실제 해결 함수에선 이 함수를 이용한 for문을 작성하였다.

javascript
const queryOperation = (arr, query) => { const slicedArr = arr.slice(query[0], query[1] + 1) const filteredArr = slicedArr.filter(value => value > query[2]) if(!filteredArr.length) return -1; const minValue = filteredArr.reduce((acc, cur) => { if(acc > cur) acc = cur; return acc; },filteredArr[0]) return minValue; } function solution(arr, queries) { const result = []; for(let i = 0; i<queries.length; i++){ result.push(queryOperation(arr, queries[i])) } return result; }

30-1. sort를 활용

reduce외에도 좋은 메소드가 많다. sort가 그 중 하나로, 개인적으로는 지나치게 한줄로 끝내려는 것처럼 보이기도 하지만 본질적으로 좋은 풀이임은 부인하기 어렵다. 사실, 줄줄 늘어쓴 내 코드보다 훨씬 잘 읽힌다.

javascript
function solution(arr, queries) { return queries.map(([s, e, k]) => arr.slice(s, e + 1).filter((n) => n > k).sort((a, b) => a - b)[0] || -1); }