0%

오늘 한 것

코딩 테스트 연습

부족한 금액 계산하기

문제 설명

새로 생긴 놀이기구는 인기가 매우 많아 줄이 끊이질 않습니다. 이 놀이기구의 원래 이용료는 price원 인데, 놀이기구를 N 번 째 이용한다면 원래 이용료의 N배를 받기로 하였습니다. 즉, 처음 이용료가 100이었다면 2번째에는 200, 3번째에는 300으로 요금이 인상됩니다.
놀이기구를 count번 타게 되면 현재 자신이 가지고 있는 금액에서 얼마가 모자라는지를 return 하도록 solution 함수를 완성하세요.
단, 금액이 부족하지 않으면 0을 return 하세요.

나의 답

1
2
3
4
5
6
7
8
9
10
11
function solution(price, money, count) {
let sum = 0;

for (let i = 1; i <= count; i++) {
sum += price * i;
}

return sum > money ? sum - money : 0;
}

solution(3, 20, 4); // 10

감탄한 타인의 답

1
2
3
4
function solution(price, money, count) {
const tmp = (price * count * (count + 1)) / 2 - money;
return tmp > 0 ? tmp : 0;
}
  • 가우스 공식 활용. 수학을 잘하는건 도움이 된다.

심심해서 쉬운 문제를 풀어봤다. 요즘 기술 면접 준비 등 다른 것들에 신경쓰다 보니 코딩을 많이 안하는 듯한 느낌을 받았다. 놓지말자.

출처: programmers

Nyong’s GitHub

오늘 한 것

코딩 테스트 연습

신규 아이디 추천

문제 설명

다음과 같이 7단계의 순차적인 처리 과정을 통해 신규 유저가 입력한 아이디가 카카오 아이디 규칙에 맞는 지 검사하고 규칙에 맞지 않은 경우 규칙에 맞는 새로운 아이디를 추천해 주려고 합니다.
신규 유저가 입력한 아이디가 new_id 라고 한다면,

1
2
3
4
5
6
7
8
1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다.
2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.
3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
5단계 new_id가 빈 문자열이라면, new_id에 "a"를 대입합니다.
6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function solution(new_id) {
const first = new_id.toLowerCase();

const second = first.replace(/[^\w-.]/g, "");

const third = second.replace(/\.{2,}/g, ".");

const fourth = third.replace(/^\.|\.$/g, "");

const fifth = !fourth.length ? "a" : fourth;

const sixth = fifth.slice(0, 15).replace(/^\.|\.$/g, "");

const seventh =
sixth.length > 2 ? sixth : sixth + sixth.slice(-1).repeat(3 - sixth.length);

return seventh;
}

solution("...!@BaT#*..y.abcdefghijklm"); // "bat.y.abcdefghi"
  • 정규식만 엄청 썼다.. 어렵지는 않지만 복잡한 정규식 규칙을 찾느라 애썼다.

감탄한 타인의 답

1
2
3
4
5
6
7
8
9
10
11
12
function solution(new_id) {
const answer = new_id
.toLowerCase() // 1
.replace(/[^\w-_.]/g, "") // 2
.replace(/\.+/g, ".") // 3
.replace(/^\.|\.$/g, "") // 4
.replace(/^$/, "a") // 5
.slice(0, 15)
.replace(/\.$/, ""); // 6
const len = answer.length;
return len > 2 ? answer : answer + answer.charAt(len - 1).repeat(3 - len);
}
  • 대체적으로 비슷했지만 체이닝으로 간결하다.
  • 개인적으로 변수에 할당함으로써 순차에 대한 명확한 표현이 있는 내 코드가 더 좋다.
1
2
3
4
5
6
7
8
9
10
11
const solution = (new_id) => {
const id = new_id
.toLowerCase()
.replace(/[^\w\d-_.]/g, "")
.replace(/\.{2,}/g, ".")
.replace(/^\.|\.$/g, "")
.padEnd(1, "a")
.slice(0, 15)
.replace(/^\.|\.$/g, "");
return id.padEnd(3, id[id.length - 1]);
};
  • String.prototype.padEnd()는 처음 알았다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // str.padEnd(targetLength [, padString])

    'Breaded Mushrooms'.padEnd(25, '.')); // "Breaded Mushrooms........"

    '200'.padEnd(5); // "200 "

    "abc".padEnd(10); // "abc "
    "abc".padEnd(10, "foo"); // "abcfoofoof"
    "abc".padEnd(6, "123456"); // "abc123"
    "abc".padEnd(1); // "abc"
    • targetLenth는 목표 문자열 길이이다. 문자열이 원하는 길이 보다 짧다면 두 번째 매개변수에 문자열을 넣어서 원하는 길이만큼 채울 수 있다. 두 번째 매개변수는 옵션으로 넣지않으면 공백문자가 기본값.
    • 문제에서 5단계 빈 문자열이라면 'a'를 채워넣는 부분은 목표 문자열 길이가 1이며, 길이 1까지 a를 채워넣고 싶기 때문에 .padEnd(1, "a")로 표현한다.

출처: programmers

Nyong’s GitHub

오늘 한 것

코딩 테스트 연습

숫자 문자열과 영단어

문제 설명

다음은 숫자의 일부 자릿수를 영단어로 바꾸는 예시입니다.

1478 → “one4seveneight”
234567 → “23four5six7”
10203 → “1zerotwozero3”
이렇게 숫자의 일부 자릿수가 영단어로 바뀌어졌거나, 혹은 바뀌지 않고 그대로인 문자열 s가 매개변수로 주어집니다. s가 의미하는 원래 숫자를 return 하도록 solution 함수를 완성해주세요.

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function solution(s) {
const reg = /[0-9]|zero|one|two|three|four|five|six|seven|eight|nine/g;

return +s
.match(reg)
.map((v) => {
switch (v) {
case "zero":
return 0;
case "one":
return 1;
case "two":
return 2;
case "three":
return 3;
case "four":
return 4;
case "five":
return 5;
case "six":
return 6;
case "seven":
return 7;
case "eight":
return 8;
case "nine":
return 9;
default:
return v;
}
})
.join("");
}

console.log(solution("one4seveneight")); // 1478
  • 무난한 문제

감탄한 타인의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function solution(s) {
s = s
.replace(/zero/gi, 0)
.replace(/one/gi, 1)
.replace(/two/gi, 2)
.replace(/three/gi, 3)
.replace(/four/gi, 4)
.replace(/five/gi, 5)
.replace(/six/gi, 6)
.replace(/seven/gi, 7)
.replace(/eight/gi, 8)
.replace(/nine/gi, 9);
return parseInt(s);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function solution(s) {
let numbers = [
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
];
var answer = s;

for (let i = 0; i < numbers.length; i++) {
let arr = answer.split(numbers[i]);
answer = arr.join(i);
}

return Number(answer);
}

출처: programmers

Nyong’s GitHub

오늘 한 것

코딩 테스트 연습

로또의 최고 순위와 최저 순위

문제 설명

로또 6/45(이하 ‘로또’로 표기)는 1부터 45까지의 숫자 중 6개를 찍어서 맞히는 대표적인 복권입니다. 아래는 로또의 순위를 정하는 방식입니다. 1

순위 당첨 내용
1 6개 번호가 모두 일치
2 5개 번호가 일치
3 4개 번호가 일치
4 3개 번호가 일치
5 2개 번호가 일치
6(낙첨) 그 외
로또를 구매한 민우는 당첨 번호 발표일을 학수고대하고 있었습니다. 하지만, 민우의 동생이 로또에 낙서를 하여, 일부 번호를 알아볼 수 없게 되었습니다. 당첨 번호 발표 후, 민우는 자신이 구매했던 로또로 당첨이 가능했던 최고 순위와 최저 순위를 알아보고 싶어 졌습니다.
알아볼 수 없는 번호를 0으로 표기하기로 하고, 민우가 구매한 로또 번호를 담은 배열 lottos, 당첨 번호를 담은 배열 win_nums가 매개변수로 주어집니다. 이때, 당첨 가능한 최고 순위와 최저 순위를 차례대로 배열에 담아서 return 하도록 solution 함수를 완성해주세요.

제한사항

  • lottos는 길이 6인 정수 배열입니다.
  • lottos의 모든 원소는 0 이상 45 이하인 정수입니다.
    • 0은 알아볼 수 없는 숫자를 의미합니다.
    • 0을 제외한 다른 숫자들은 lottos에 2개 이상 담겨있지 않습니다.
    • lottos의 원소들은 정렬되어 있지 않을 수도 있습니다.
  • win_nums은 길이 6인 정수 배열입니다.
  • win_nums의 모든 원소는 1 이상 45 이하인 정수입니다.
    • win_nums에는 같은 숫자가 2개 이상 담겨있지 않습니다.
    • win_nums의 원소들은 정렬되어 있지 않을 수도 있습니다.

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function solution(lottos, winNums) {
const zeroCnt = lottos.filter((v) => v === 0).length;
const myWinNum = lottos.filter((v) => winNums.includes(v)).length;

switch (myWinNum) {
case 0:
return [zeroCnt ? 7 - zeroCnt : 6, 6];
case 1:
case 2:
case 3:
case 4:
case 5:
return [7 - myWinNum - zeroCnt, 7 - myWinNum];
case 6:
return [1, 1];
default:
throw new Error("params error");
}
}

console.log(solution([44, 1, 0, 0, 31, 25], [31, 10, 45, 1, 6, 19])); // [3, 5]
  • 어렵지는 않았다. 괜히 어렵게 푼 기분이 든다.
1
2
3
4
5
6
7
8
9
10
function solution(lottos, winNums) {
const zeroCnt = lottos.filter((v) => v === 0).length;
const myWinNum = lottos.filter((v) => winNums.includes(v)).length;

if (myWinNum === 0) return [zeroCnt ? 7 - zeroCnt : 6, 6];
if (myWinNum > 0 && myWinNum < 6) {
return [7 - myWinNum - zeroCnt, 7 - myWinNum];
}
return [1, 1];
}
  • if가 더 나으려나… 순위 경우의 수가 많으면 if문이 좋을 듯 하다.

감탄한 타인의 답

1
2
3
4
5
6
7
8
9
10
function solution(lottos, win_nums) {
const rank = [6, 6, 5, 4, 3, 2, 1];

let minCount = lottos.filter((v) => win_nums.includes(v)).length;
let zeroCount = lottos.filter((v) => !v).length;

const maxCount = minCount + zeroCount;

return [rank[maxCount], rank[minCount]];
}
  • 어렵게 푼 기분이 틀리지 않았다. 조건문이 필요가 없다. 다만 rank의 경우의 수가 많아지면 if가 나을지도..??

폰켓몬

문제 설명

당신은 폰켓몬을 잡기 위한 오랜 여행 끝에, 홍 박사님의 연구실에 도착했습니다. 홍 박사님은 당신에게 자신의 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2마리를 가져가도 좋다고 했습니다.
홍 박사님 연구실의 폰켓몬은 종류에 따라 번호를 붙여 구분합니다. 따라서 같은 종류의 폰켓몬은 같은 번호를 가지고 있습니다. 예를 들어 연구실에 총 4마리의 폰켓몬이 있고, 각 폰켓몬의 종류 번호가 [3번, 1번, 2번, 3번]이라면 이는 3번 폰켓몬 두 마리, 1번 폰켓몬 한 마리, 2번 폰켓몬 한 마리가 있음을 나타냅니다. 이때, 4마리의 폰켓몬 중 2마리를 고르는 방법은 다음과 같이 6가지가 있습니다.

  1. 첫 번째(3번), 두 번째(1번) 폰켓몬을 선택
  2. 첫 번째(3번), 세 번째(2번) 폰켓몬을 선택
  3. 첫 번째(3번), 네 번째(3번) 폰켓몬을 선택
  4. 두 번째(1번), 세 번째(2번) 폰켓몬을 선택
  5. 두 번째(1번), 네 번째(3번) 폰켓몬을 선택
  6. 세 번째(2번), 네 번째(3번) 폰켓몬을 선택

이때, 첫 번째(3번) 폰켓몬과 네 번째(3번) 폰켓몬을 선택하는 방법은 한 종류(3번 폰켓몬 두 마리)의 폰켓몬만 가질 수 있지만, 다른 방법들은 모두 두 종류의 폰켓몬을 가질 수 있습니다. 따라서 위 예시에서 가질 수 있는 폰켓몬 종류 수의 최댓값은 2가 됩니다.
당신은 최대한 다양한 종류의 폰켓몬을 가지길 원하기 때문에, 최대한 많은 종류의 폰켓몬을 포함해서 N/2마리를 선택하려 합니다. N마리 폰켓몬의 종류 번호가 담긴 배열 nums가 매개변수로 주어질 때, N/2마리의 폰켓몬을 선택하는 방법 중, 가장 많은 종류의 폰켓몬을 선택하는 방법을 찾아, 그때의 폰켓몬 종류 번호의 개수를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • nums는 폰켓몬의 종류 번호가 담긴 1차원 배열입니다.
  • nums의 길이(N)는 1 이상 10,000 이하의 자연수이며, 항상 짝수로 주어집니다.
  • 폰켓몬의 종류 번호는 1 이상 200,000 이하의 자연수로 나타냅니다.
  • 가장 많은 종류의 폰켓몬을 선택하는 방법이 여러 가지인 경우에도, 선택할 수 있는 폰켓몬 종류 개수의 최댓값 하나만 return 하면 됩니다.

나의 답

1
2
3
4
5
6
7
8
function solution(nums) {
const chooseMonNum = nums.length / 2;
const monArr = [...new Set(nums)].length;

return chooseMonNum > monArr ? monArr : chooseMonNum;
}

console.log(solution([3, 1, 2, 3])); // 2
  • 길고 긴 설명에 비하여 간단한 문제

출처: programmers

Nyong’s GitHub

오늘 한 것

코딩 테스트 연습

소수 찾기

문제 설명

1부터 입력받은 숫자 n 사이에 있는 소수의 개수를 반환하는 함수, solution을 만들어 보세요.

소수는 1과 자기 자신으로만 나누어지는 수를 의미합니다.
(1은 소수가 아닙니다.)

제한사항

  • n은 2이상 1000000이하의 자연수입니다.

나의 답

  • 에라토스테네스의 체를 참고하여 구현하였다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 성능 이슈
function firstSolution(n) {
const min = Math.sqrt(n);
const array = Array.from({ length: n - 1 }, (_, i) => i + 2);
const minArr = Array.from({ length: min }, (_, i) => i + 1);
let primeArr = array;

for (let i = 0; i < minArr.length; i++) {
let cnt = 0;
for (let j = 1; j < minArr[i]; j++) {
if (minArr[i] % j === 0) cnt++;
}
if (cnt === 1) {
primeArr = primeArr.filter((v) => v % minArr[i] !== 0 || v === minArr[i]);
}
}

return primeArr.length;
}

console.log(firstSolution(10)); // 4
console.log(firstSolution(5)); // 3
  • 처음에는 성능은 신경쓰지 않고 생각 나는데로 만들었다.
  • 효율성 검사에서 탈락했다. 지금 다시 보니 그럴만한 코드다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 성능 이슈
function secondSolution(n) {
const min = Math.sqrt(n);
const primeArr = [];

let currentArr = Array.from({ length: n - 1 }, (_, i) => i + 2);

const isPrime = (num) => {
let cnt = 0;
for (let i = 1; i < num; i++) {
if (num % i === 0) cnt++;
}

return cnt === 1;
};

for (let i = 2; i < min; i++) {
if (isPrime(i)) {
primeArr.push(i);
currentArr = currentArr.filter((v) => v % i !== 0);
}
}

return [...primeArr, ...currentArr].length;
}

console.log(secondSolution(10)); // 4
console.log(secondSolution(5)); // 3
  • 나름 다시 고민해서 구현한 코드이다.
  • 소수 판별 함수를 따로 분리하고 filter()메서드로 소수의 배수들을 제한다.
  • 하지만 효율성 검사에서 역시나 실패했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const currentArr = Array.from({ length: n - 1 }, (_, i) => i + 2);

const isPrime = (num) => {
let cnt = 0;
for (let i = 1; i < num; i++) {
if (num % i === 0) cnt++;
}

return cnt === 1;
};

for (let i = 2; i * i < n; i++) {
if (isPrime(i)) {
for (let j = i + i - 2; j < n; j += i) currentArr[j] = 0;
}
}

return currentArr.filter((v) => v).length;

console.log(thirdSolution(10)); // 4
console.log(thirdSolution(5)); // 3
  • 정말 불필요한 부분은 최대한 제거했다. Math.sqrt() 메서드 조차도 뺴버렸다.
  • filter()로 순회하던 과정을 제거하고 for문을 통해 소수의 배수(j += i)로 순회하며 값 0을 할당한 뒤, 마지막에 filter()로 모든 0을 삭제한다.
  • 위 세가지 경우에 대해 성능을 검사해보니, 3번이 다른 두 경우보다 확실히 유의미한 성능차를 보였다.
  • 데이터가 커질 수록 자료구조를 순회하는 방법이 중요함을 다시금 느낀다.

소수 만들기

문제 설명

주어진 숫자 중 3개의 수를 더했을 때 소수가 되는 경우의 개수를 구하려고 합니다. 숫자들이 들어있는 배열 nums가 매개변수로 주어질 때, nums에 있는 숫자들 중 서로 다른 3개를 골라 더했을 때 소수가 되는 경우의 개수를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • nums에 들어있는 숫자의 개수는 3개 이상 50개 이하입니다.
  • nums의 각 원소는 1 이상 1,000 이하의 자연수이며, 중복된 숫자가 들어있지 않습니다.

나의 답

1
2
3
4
5
6
7
8
const isPrime = (num) => {
let cnt = 0;
for (let i = 1; i < num; i++) {
if (num % i === 0) cnt++;
}

return cnt === 1;
};
  • 소수인지 판별하는 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 성능 우세
function solution(nums) {
const odd = [];
const even = [];

for (let i = 0; i < nums.length; i++) {
if (nums[i] % 2 === 0) even.push(nums[i]);
else odd.push(nums[i]);
}

let cnt = 0;

for (let i = 0; i < even.length - 1; i++) {
for (let j = i + 1; j < even.length; j++) {
for (let k = 0; k < odd.length; k++) {
if (isPrime(even[i] + even[j] + odd[k])) cnt++;
}
}
}

for (let i = 0; i < odd.length - 2; i++) {
for (let j = i + 1; j < odd.length - 1; j++) {
for (let k = j + 1; k < odd.length; k++) {
if (isPrime(odd[i] + odd[j] + odd[k])) cnt++;
}
}
}

return cnt;
}

console.log(solution([1, 2, 3, 4])); // 1
console.log(solution([1, 2, 7, 6, 4])); // 4
  • 2를 제외한 모든 소수는 홀수이다. 본 문제에서는 중복되지 않는 1 이상의 세 수를 더한 소수를 구하기 때문에 발생 가능한 수 중 가장 작은 소수는 7이다.
  • 짝수와 홀수를 구분한 후, 홀수를 만들 수 있는 조합은 짝수 + 짝수 + 홀수홀수 + 홀수 + 홀수다.
  • 소수 발생 가능성이 있는 세수의 조합만을 계산하여 소수인지 판별한다.
  • 코드의 길이는 길지만 아래 단순 계산하는 코드보다 유의미하게 높은 성능 차이를 보였다. (1부터 100까지의 배열에서 약 2배의 성능 차이)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function solution(nums) {
let answer = 0;

for (let i = 0; i < nums.length - 2; i++) {
for (let j = i + 1; j < nums.length - 1; j++) {
for (let k = j + 1; k < nums.length; k++) {
if (isPrime(nums[i] + nums[j] + nums[k])) answer++;
}
}
}

return answer;
}

console.log(solution([1, 2, 3, 4])); // 1
console.log(solution([1, 2, 7, 6, 4])); // 4
  • 모든 경우의 수를 생성한 후, 소수인지 판별하는 단순한 알고리즘이다.
  • 첫번째 경우보다 깔끔해보이지만 성능은 모든 경우의 수를 계산하기 떄문에 좋지 못하다.

약수의 개수와 덧셈

문제 설명

두 정수 left와 right가 매개변수로 주어집니다. left부터 right까지의 모든 수들 중에서, 약수의 개수가 짝수인 수는 더하고, 약수의 개수가 홀수인 수는 뺀 수를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • 1 ≤ left ≤ right ≤ 1,000

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
function solution(left, right) {
let sum = 0;

for (let i = left; i <= right; i++) {
let cnt = 0;
for (let j = 1; j <= i; j++) {
if (i % j === 0) cnt++;
}
cnt % 2 === 0 ? (sum += i) : (sum -= i);
}

return sum;
}
  • 삼항 조건 연산자가 편하긴 한데, if..else 문을 쓰는게 더 직관적인지 애매하다.
  • 쉬운 문제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function solution(left, right) {
let num = [];

for (let i = left; i <= right; i++) {
let cnt = 0;
for (let j = 1; j <= i; j++) {
if (i % j === 0) cnt++;
}
num.push([i, cnt]);
}

return num.reduce(
(acc, cur) => (cur[1] % 2 === 0 ? acc + cur[0] : acc - cur[0]),
0
);
}
  • 배열 메서드 연습하고 싶어서 꼬아서 놀았다. 의미없다.

감탄한 타인의 답

1
2
3
4
5
6
7
8
9
10
11
function solution(left, right) {
var answer = 0;
for (let i = left; i <= right; i++) {
if (Number.isInteger(Math.sqrt(i))) {
answer -= i;
} else {
answer += i;
}
}
return answer;
}
  • 제곱수(자연수를 제곱해 구해지는 수)의 약수 개수는 항상 홀수이다…같은 수를 두번 곱한 경우의 짝이 없는 약수가 1개 생기기 때문…수학 상식도 재밌다..

두 개 뽑아서 더하기

문제 설명

정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.

제한사항

  • numbers의 길이는 2 이상 100 이하입니다.
    • numbers의 모든 수는 0 이상 100 이하입니다

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
function solution(numbers) {
const answer = [];

for (let i = 0; i < numbers.length; i++) {
for (let j = i + 1; j < numbers.length; j++) {
if (i !== j) answer.push(numbers[i] + numbers[j]);
}
}

return [...new Set(answer.sort((a, b) => a - b))];
}

console.log(solution([2, 1, 3, 4, 1])); // [2, 3, 4, 5, 6, 7]
  • 처음에는 j = 0으로 선언하여 불필요한 연산을 했다. 한번만 더 생각하자.

나누어 떨어지는 숫자 배열

문제 설명

array의 각 element 중 divisor로 나누어 떨어지는 값을 오름차순으로 정렬한 배열을 반환하는 함수, solution을 작성해주세요.
divisor로 나누어 떨어지는 element가 하나도 없다면 배열에 -1을 담아 반환하세요.

제한사항

  • arr은 자연수를 담은 배열입니다.
  • 정수 i, j에 대해 i ≠ j 이면 arr[i] ≠ arr[j] 입니다.
  • divisor는 자연수입니다.
  • array는 길이 1 이상인 배열입니다.

나의 답

1
2
3
4
5
6
7
function solution(arr, divisor) {
const answer = arr.filter((v) => v % divisor === 0).sort((a, b) => a - b);

return answer.length ? answer : [-1];
}

console.log(solution([5, 9, 7, 10], 5)); // [5, 10]

3진법 뒤집기

문제 설명

자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • n은 1 이상 100,000,000 이하인 자연수입니다.

나의 답

1
2
3
4
5
6
7
8
function solution(n) {
const num = n.toString(3).split("").reverse().join("");
// const num = [...n.toString(3)].reverse().join("");

return parseInt(num, 3);
}

console.log(solution(45)); // 7
  • 내장함수로 간단히 해결
1
2
3
4
5
6
7
8
function solution(n) {
const answer = [];
while (n !== 0) {
answer.unshift(n % 3);
n = Math.floor(n / 3);
}
return answer.reduce((acc, v, i) => acc + v * Math.pow(3, i), 0);
}
  • 내장 함수 없이 계산도 가능하다.

출처: programmers

Nyong’s GitHub

오늘 한 것

Git Issue Render 컴포넌트 진행

git API를 가져와서 렌더하는 페이지를 만들고 있다. 처음에는 바닐라로 시작했지만 오늘 typeScript로 바꿨다.
template을 지정하지 않고 CRA를 생성했기 때문에 npm install --save typescript @types/node @types/react @types/react-dom @types/jest로 npm 설치 후 진행하였다.

typeScript Generic

tsconfig.json로 수정하고 기존 만든 바닐라 js를 타입 지정하며 ts로 변환 중에, 바닐라 때 만들었던 useFetchDate 커스텀 훅이 가장 말썽이었어서 기록한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { useState, useEffect } from "react";

export const STATUS = {
idle: "idle",
pending: "pending",
resolved: "resolved",
rejected: "rejected",
};

const { idle, pending, resolved, rejected } = STATUS;

export function useFetchData(api) {
const [status, setStatus] = useState(idle);
const [error, setError] = useState(null);
const [data, setData] = useState(null);

useEffect(() => {
setStatus(idle);
setError(null);

const fetchData = async () => {
setStatus(pending);
try {
const response = await fetch(api);
const json = await response.json();
setStatus(resolved);
setData(json);
console.log(json);
} catch (error) {
setStatus(rejected);
setError(error);
console.log(error);
}
};

fetchData(api);
}, [api]);

return [status, error, data];
}
  • 본래 js에서 이 커스텀 훅은 api를 통해 결과를 반환하는 훅이다.
  • 다양한 api 통신에서 사용하기 위해 만들었으며 당연히 반환값 인수로 주어지는 api에 따라 다르다.

typeScript로 변환하는 과정에서 제일 문제였던 부분은 반환값이 api에 따라 다르다는 것이다.
간만에 ts를 사용하다 보니까 우왕좌왕하며 커스텀 훅을 반환 결과에 따라 만들어야되나 고민 중 Generic이 생각나서 해결할 수 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { error } from "constant/type";
import { useState, useEffect } from "react";

export const STATUS = {
idle: "idle",
pending: "pending",
resolved: "resolved",
rejected: "rejected",
};

const { idle, pending, resolved, rejected } = STATUS;

export function useFetchData<T>>(api: string) {
const [status, setStatus] = useState<string>(idle);
const [error, setError] = useState<error | null>(null);
const [data, setData] = useState<T | null>(null);

useEffect(() => {
setStatus(idle);
setError(null);

const fetchData = async (api: string) => {
setStatus(pending);
try {
const response = await fetch(api);
const json = await response.json();
setStatus(resolved);
setData(json);
console.log(json);
} catch (error) {
setStatus(rejected);
setError(error);
console.log(error);
}
};

fetchData(api);
}, [api]);

return [status, error, data] as [string, error | null, T | null];
}
  • 또한 FetchData()에서 js에서는 가변인자 함수로 사용했지만 typeScript는 가변인자 함수가 불가능한 것 같다.
  • 간만에 typeScript를 사용하니까 다시 헷갈린다. 꾸준히 하자 꾸준히!

코딩 테스트 연습

예산

문제 설명

S사에서는 각 부서에 필요한 물품을 지원해 주기 위해 부서별로 물품을 구매하는데 필요한 금액을 조사했습니다. 그러나, 전체 예산이 정해져 있기 때문에 모든 부서의 물품을 구매해 줄 수는 없습니다. 그래서 최대한 많은 부서의 물품을 구매해 줄 수 있도록 하려고 합니다.

물품을 구매해 줄 때는 각 부서가 신청한 금액만큼을 모두 지원해 줘야 합니다. 예를 들어 1,000원을 신청한 부서에는 정확히 1,000원을 지원해야 하며, 1,000원보다 적은 금액을 지원해 줄 수는 없습니다.

부서별로 신청한 금액이 들어있는 배열 d와 예산 budget이 매개변수로 주어질 때, 최대 몇 개의 부서에 물품을 지원할 수 있는지 return 하도록 solution 함수를 완성해주세요.

제한사항

  • d는 부서별로 신청한 금액이 들어있는 배열이며, 길이(전체 부서의 개수)는 1 이상 100 이하입니다.
  • d의 각 원소는 부서별로 신청한 금액을 나타내며, 부서별 신청 금액은 1 이상 100,000 이하의 자연수입니다.
  • budget은 예산을 나타내며, 1 이상 10,000,000 이하의 자연수입니다.

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// for문 사용
function solutionFor(d, budget) {
let sum = 0;
let cnt = 0;

d.sort((a, b) => a - b);

for (cnt = 0; cnt < d.length; cnt++) {
if (budget - sum >= d[cnt]) {
sum += d[cnt];
} else return cnt;
}

return cnt;
}

// Array.prototype.reduce 사용
function solutionReduce(d, budget) {
let cnt = 0;
d.sort((a, b) => a - b).reduce((acc, cur) => {
if (budget - acc >= cur) {
cnt++;
return acc + cur;
}
return acc;
}, 0);

return cnt;
}

// Array.prototype.filter 사용
function solutionFilter(d, budget) {
return d
.sort((a, b) => a - b)
.filter((v) => (budget - v >= 0 ? ((budget -= v), true) : false)).length;
}

console.log(solutionFor([1, 3, 2, 5, 4], 9)); // 3
console.log(solutionReduce([1, 3, 2, 5, 4], 9)); // 3
console.log(solutionFilter([1, 3, 2, 5, 4], 9)); // 3
  • 가볍게 풀만한 문제

출처: programmers

Nyong’s GitHub

오늘 한 것

공부한 javaScript 코딩 문제

최대공약수와 최소공배수

문제 설명

두 수를 입력받아 두 수의 최대공약수와 최소공배수를 반환하는 함수, solution을 완성해 보세요.
배열의 맨 앞에 최대공약수, 그다음 최소공배수를 넣어 반환하면 됩니다.
예를 들어 두 수 3, 12의 최대공약수는 3, 최소공배수는 12이므로 solution(3, 12)는 [3, 12]를 반환해야 합니다.

제한사항

  • 두 수는 1이상 1000000이하의 자연수입니다.

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 최대공약수를 구하는 calGCF 함수
function calGCF(bigNum, smallNum) {
const factor = [];

for (let i = 2; i <= smallNum; i++) {
if (bigNum % i === 0 && smallNum % i === 0) {
bigNum /= i;
smallNum /= i;
factor.push(i);
i = 1;
}
}

return factor.reduce((acc, cur) => acc * cur, 1);
}

function solution(n, m) {
const GCF = n > m ? calGCF(n, m) : calGCF(m, n);
const LCM = GCF * (n / GCF) * (m / GCF);
// LCM = n * m / GCF와 동일하지만 보다 명확한 식을 위해

return [GCF, LCM];
}

console.log(solution(3, 12)); // [3, 12]
  • 중등 수학까지 거슬러 올라갔었다…
  • 수학적으로 접근해서 차근차근 풀면 어렵지 않은 문제.
  • calGCF에서 인수에 직접 접근하여 재할당하는게 마음에 썩 내키지는 않는다.

감탄한 타인의 답

1
2
3
4
5
6
7
8
9
10
11
function greatestCommonDivisor(a, b) {
return b ? greatestCommonDivisor(b, a % b) : Math.abs(a);
}
function leastCommonMultipleOfTwo(a, b) {
return (a * b) / greatestCommonDivisor(a, b);
}
function gcdlcm(a, b) {
return [greatestCommonDivisor(a, b), leastCommonMultipleOfTwo(a, b)];
}

console.log(gcdlcm(3, 12));
1
2
3
4
5
function gcdlcm(a, b) {
var r;
for (var ab = a * b; (r = a % b); a = b, b = r) {}
return [b, ab / b];
}

문자열 내 마음대로 정렬하기

문제 설명

문자열로 구성된 리스트 strings와, 정수 n이 주어졌을 때, 각 문자열의 인덱스 n번째 글자를 기준으로 오름차순 정렬하려 합니다. 예를 들어 strings가 [“sun”, “bed”, “car”]이고 n이 1이면 각 단어의 인덱스 1의 문자 “u”, “e”, “a”로 strings를 정렬합니다.

제한사항

  • strings는 길이 1 이상, 50이하인 배열입니다.
  • strings의 원소는 소문자 알파벳으로 이루어져 있습니다.
  • strings의 원소는 길이 1 이상, 100이하인 문자열입니다.
  • 모든 strings의 원소의 길이는 n보다 큽니다.
  • 인덱스 1의 문자가 같은 문자열이 여럿 일 경우, 사전순으로 앞선 문자열이 앞쪽에 위치합니다.

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function solution(strings, n) {
return strings.sort((a, b) => {
if (a[n] < b[n]) {
return -1;
}
if (a[n] > b[n]) {
return 1;
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}

return 0;
});
}

console.log(solution(["sun", "bed", "car"], 1)); // ['car', 'bed', 'sun']
console.log(solution(["abce", "abcd", "cdx"], 2)); // ['abcd', 'abce', 'cdx']
  • sort()메서드에 대한 문제. compareFunction부분에 대해 더 깊게 알았다.

감탄한 타인의 답

1
2
3
4
5
function solution(strings, n) {
return strings.sort((s1, s2) =>
s1[n] === s2[n] ? s1.localeCompare(s2) : s1[n].localeCompare(s2[n])
);
}
  • String.prototype.localeCompare() 까지 사용한 답안이 있었다. 문자열과 문자열을 정렬 순서에 따라 비교하여 -1, 0 ,1 을 return한다.
    1
    2
    3
    console.log("a".localeCompare("b")); // -1
    console.log("b".localeCompare("a")); // 1
    console.log("b".localeCompare("b")); // 0
  • 내 답안에서도 localeCompare()사용하면 비교함수가 훨씬 간단해질 수 있다.

출처: programmers

Nyong’s GitHub

오늘 한 것

공부한 javaScript 코딩 문제

체육복

문제 설명

함수 solution은 정수 n을 매개변수로 입력받습니다.
n의 각 자릿수를 큰것부터 작은 순으로 정렬한 새로운 정수를 리턴해주세요.
예를들어 n이 118372면 873211을 리턴하면 됩니다.

제한사항

  • n은 1이상 8000000000 이하인 자연수입니다.

나의 답

1
2
3
4
5
function solution(n) {
return +(n + "").split("").sort().reverse().join("");
}

solution(118372); // 873211
  • 쉬운 문제
  • 1단계부터 다 풀어보자.

출처: programmers

Nyong’s GitHub

오늘 한 것

공부한 javaScript 코딩 문제

체육복

문제 설명

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다.
학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다.
예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.

전체 학생의 수 n,
체육복을 도난당한 학생들의 번호가 담긴 배열 lost,
여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때,
체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 전체 학생의 수는 2명 이상 30명 이하입니다.
  • 체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
  • 여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function solution(n, lost, reserve) {
let students = n - lost.length;

lost.sort((a, b) => a - b);
reserve.sort((a, b) => a - b);

for (let i = 0; i < lost.length; i++) {
const idx = reserve.indexOf(lost[i]);

if (idx !== -1) {
students += 1;
lost.splice(i, 1);
reserve.splice(idx, 1);
i--;
}
}

for (let i = 0; i < lost.length; i++) {
for (let j = 0; j < reserve.length; j++) {
if (
reserve[j] === lost[i] ||
reserve[j] - 1 === lost[i] ||
reserve[j] + 1 === lost[i]
) {
students += 1;
reserve.splice(j, 1);
break;
}
}
}

return students;
}

console.log(solution(5, [2, 4], [1, 3, 5])); // 5
  • 테스트 케이스에 변수가 너무 많았다.
  • 문제를 잘 읽고 여러가지 예외 사항을 고려하자.

감탄한 타인의 답

1
2
3
4
5
6
7
8
9
10
function solution(n, lost, reserve) {
return (
n -
lost.filter((a) => {
const b = reserve.find((r) => Math.abs(r - a) <= 1);
if (!b) return true;
reserve = reserve.filter((r) => r !== b);
}).length
);
}
  • 자괴감이 든다……………나도 저런 코드를 위해 열심히 열코 즐코..

완주하지 못한 선수

문제 설명

수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.

마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
  • completion의 길이는 participant의 길이보다 1 작습니다.
  • 참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
  • 참가자 중에는 동명이인이 있을 수 있습니다.

나의 답

1
2
3
4
5
6
7
function solution(participant, completion) {
return participant
.filter((v, i, arr) => completion.indexOf(v) === -1 || arr.indexOf(v) !== i)
.join();
}

console.log(solution(["leo", "kiki", "eden"], ["eden", "kiki"])); // 'leo'
1
2
3
4
5
6
7
8
9
10
11
12
function solution(participant, completion) {
participant.sort();
completion.sort();

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

return undefined;
}

console.log(solution(["leo", "kiki", "eden"], ["eden", "kiki"])); // 'leo'
  • 첫 풀이는 성능 문제가 있었다.
  • 두 번째 방법으로 정렬 후 단순 비교하였다.

문자열 내림차순으로 배치하기

문제 설명

문자열 s에 나타나는 문자를 큰것부터 작은 순으로 정렬해 새로운 문자열을 리턴하는 함수, solution을 완성해주세요.
s는 영문 대소문자로만 구성되어 있으며, 대문자는 소문자보다 작은 것으로 간주합니다.

제한사항

  • str은 길이 1 이상인 문자열입니다.

나의 답

1
2
3
4
5
function solution(s) {
return s.split("").sort().reverse().join("");
}

console.log(solution("Zbcdefg")); // gfedcbZ
  • 간단한 문제.

출처: programmers

Nyong’s GitHub

오늘 한 것

공부한 javaScript 코딩 문제

시저 암호

문제 설명

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 “AB”는 1만큼 밀면 “BC”가 되고, 3만큼 밀면 “DE”가 됩니다. “z”는 1만큼 밀면 “a”가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.

제한사항

  • 공백은 아무리 밀어도 공백입니다.
  • s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.
  • s의 길이는 8000이하입니다.
  • n은 1 이상, 25이하인 자연수입니다.

나의 답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function solution(s, n) {
return s
.split("")
.map((v) => v.charCodeAt())
.map((v) => {
if (v === 32) return String.fromCharCode(32);
if (v <= 90) {
return v + n > 90
? String.fromCharCode(v + n - 26)
: String.fromCharCode(v + n);
}
return v + n > 122
? String.fromCharCode(v + n - 26)
: String.fromCharCode(v + n);
})
.join("");
}

console.log(solution("AB", 1)); // BC
  • 간만에 아스키코드 관련 문제였다.
  • 쉽지만 귀찮은 문제…

출처: programmers

Nyong’s GitHub