Day32: 등수 매기기, 저주의 숫자 3, 문자열 막기, 유한소수 판별하기, 특이한 정렬
등수 매기기
영어 점수와 수학 점수의 평균 점수를 기준으로 학생들의 등수를 매기려고 합니다. 영어 점수와 수학 점수를 담은 2차원 정수 배열 score가 주어질 때, 영어 점수와 수학 점수의 평균을 기준으로 매긴 등수를 담은 배열을 return하도록 solution 함수를 완성해주세요.
function solution(score) {
let a = score.map(v => (v[0] + v[1]) / 2).sort((a, b) => b - a)
return score.map(v => (v[0] + v[1]) / 2).map((v, i) => v = a.indexOf(v) + 1)
}
다른 답:
function solution(score) {
return score.map((el) => {
return (
score.filter((v) => (v[0] + v[1]) / 2 > (el[0] + el[1]) / 2).length + 1
);
});
}
function solution(score) {
let avg = score.map(v => (v[0] + v[1]) / 2);
let sorted = avg.slice().sort((a, b) => b - a);
return avg.map(v => sorted.indexOf(v) + 1);
}
저주의 숫자 3
3x 마을 사람들은 3을 저주의 숫자라고 생각하기 때문에 3의 배수와 숫자 3을 사용하지 않습니다. 3x 마을 사람들의 숫자는 다음과 같습니다.
정수 n이 매개변수로 주어질 때, n을 3x 마을에서 사용하는 숫자로 바꿔 return하도록 solution 함수를 완성해주세요.
function solution(n) {
let a = 0
let i = 0
while(i < n) {
if(String(a).split('').includes('3')) {
a++
}
else if(a % 3 === 0) {
a++
}
else {
a++
i++
}
}
return a - 1
}
다른 답:
function solution(n) {
var answer = 0;
for(let i = 1; i <= n; i++){
if(i % 3 == 0){
n++;
}
if(String(i).includes("3") && i % 3 != 0){
n++
}
}
return n;
}
function solution(n) {
let arr = [];
let num = 0;
while (arr.length !== n && ++num)
if (num % 3 !== 0 && !(''+num).includes('3')) arr.push(num);
return arr.pop();
}
function solution(n) {
return [...Array(n * 3)]
.map((_, i) => i + 1)
.filter((num) => num % 3 !== 0 && !num.toString().includes("3"))[n - 1];
}
문자열 막기
문자열 "hello"에서 각 문자를 오른쪽으로 한 칸씩 밀고 마지막 문자는 맨 앞으로 이동시키면 "ohell"이 됩니다. 이것을 문자열을 민다고 정의한다면 문자열 A와 B가 매개변수로 주어질 때, A를 밀어서 B가 될 수 있다면 밀어야 하는 최소 횟수를 return하고 밀어서 B가 될 수 없으면 -1을 return 하도록 solution 함수를 완성해보세요.
function solution(A, B) {
let val = ''
let s = B.split('')
for(let j = 0; j < A.length; j++) {
if(s.join('') === A) return j
for(let i = 0; i < A.length; i++) {
if(i === 0) {
val = s[0]
s[i] = s[i + 1]
}
else if(i === s.length - 1) s[s.length - 1] = val
else s[i] = s[i + 1]
}
}
return -1
}
다른 답:
let solution = (a, b) => (b + b).indexOf(a)
function solution(A, B) {
if (A === B) return 0;
for (let i = 0; i < A.length; i++) {
A = A.slice(-1) + A.slice(0, -1);
if (A === B) return i + 1;
}
return -1;
}
function solution(A, B) {
A = [...A];
B = [...B];
let i = 0;
for(i; i < A.length; i++){
if(A.join('') === B.join('')) break;
A.unshift(A.pop());
console.log(A.join(''), B.join(''))
}
return i === A.length ? -1 : i
}
유한소수 판별하기
소수점 아래 숫자가 계속되지 않고 유한개인 소수를 유한소수라고 합니다. 분수를 소수로 고칠 때 유한소수로 나타낼 수 있는 분수인지 판별하려고 합니다. 유한소수가 되기 위한 분수의 조건은 다음과 같습니다.
기약분수로 나타내었을 때, 분모의 소인수가 2와 5만 존재해야 합니다.
두 정수 a와 b가 매개변수로 주어질 때, a/b가 유한소수이면 1을, 무한소수라면 2를 return하도록 solution 함수를 완성해주세요.
function solution(a, b) {
return String(parseFloat(a / b)).length < 15 ? 1 : 2
}
다른 답:
function solution(a, b) {
let n = 1;
for (let i = 1; i <= Math.min(a, b); i++) {
if (a % i===0 && b % i===0) n = i;
}
b /= n;
while (b % 2 === 0) b /= 2;
while (b % 5 === 0) b /= 5;
return b === 1 ? 1 : 2;
}
function solution(a, b) {
return Number((a / b).toFixed(10)) == a / b ? 1 : 2
}
특이한 정렬
정수 n을 기준으로 n과 가까운 수부터 정렬하려고 합니다. 이때 n으로부터의 거리가 같다면 더 큰 수를 앞에 오도록 배치합니다. 정수가 담긴 배열 numlist와 정수 n이 주어질 때 numlist의 원소를 n으로부터 가까운 순서대로 정렬한 배열을 return하도록 solution 함수를 완성해주세요.
function solution(numlist, n) {
let arr = [];
numlist.sort((a, b) => a - b);
new_numlist = numlist.map((a) => Math.abs(a - n));
let min_idx = -1;
for (let i = 0; i < numlist.length; i++) {
min_idx = new_numlist.lastIndexOf(Math.min(...new_numlist));
new_numlist[min_idx] = Infinity;
arr.push(numlist[min_idx]);
}
return arr;
}
다른 답:
function solution(numlist, n) {
return numlist.sort((a, b) => Math.abs(a - n) - Math.abs(b - n) || b - a);
}
function solution(numlist, n) {
numlist.sort((a,b)=>{
let n1 = Math.abs(a-n),
n2 = Math.abs(b-n);
return n1 < n2 ? -1 : n1 === n2 ? a < b ? 1 : -1 : 1;
});
return numlist;
}
function solution(numlist, n) {
var answer = [];
var num = [n, ...numlist].sort((a, b) => a-b);
var [i, j] = [num.indexOf(n)-1, num.indexOf(n)];
num.splice(num.indexOf(n), 1);
while(numlist.length !== answer.length){
if(i === -1 || Math.abs(n - num[i]) >= Math.abs(n - num[j]) || n === num[j]){
answer.push(num[j]);
j++;
}else if(j === numlist.length || Math.abs(n - num[i]) < Math.abs(n - num[j])){
answer.push(num[i]);
i--;
}
}
return answer;
}
- 왼쪽(앞으로 보내기) = a가 더 작다
- 그대로 = 같다
- 오른쪽(뒤로 보내기) = a가 더 크다
sort의 인자와 규칙
arr.sort(compareFn)에서 비교 함수(compareFn) 는 이렇게 생김:
function compareFn(a, b) {
// a와 b를 비교해서
// 음수 -> a를 b의 "앞"으로
// 0 -> a와 b의 순서 유지
// 양수 -> a를 b의 "뒤"로
}
- 정렬기는 배열에서 요소를 두 개씩 뽑아 compareFn(a, b)를 여러 번 호출해.
- 반환값이 숫자여야 하고, 그 부호(음수/0/양수) 로 “앞/그대로/뒤”를 알려줘.
왜 숫자?
“앞/뒤/그대로”를 한 번에 표현하려고 음수/0/양수라는 세 가지 신호를 쓰는 거야.
음수면 “앞”, 0이면 “그대로”, 양수면 “뒤”.
숫자 오름차순
arr.sort((a, b) => a - b);
// a-b가 음수면 a<b → a를 앞
// 0이면 a===b → 그대로
// 양수면 a>b → a를 뒤
숫자 내림차순
arr.sort((a, b) => b - a);
문자열 “사람 친화(2가 10보다 먼저)·대소문자 무시·한글” 정렬
arr.sort((a, b) => a.localeCompare(b, "ko", { numeric: true, sensitivity: "base" }));
간단 문자열(사전식) 정렬
arr.sort((a, b) => (a > b) - (a < b));
“(a > b) - (a < b)”
- JS에서 true는 1, false는 0으로 계산돼.
- 상황별 값:
- a>b 이면 1 - 0 = +1 → 양수 → a를 뒤로
- a<b 이면 0 - 1 = -1 → 음수 → a를 앞으로
- a==b 이면 0 - 0 = 0 → 0 → 그대로
즉, 이 한 줄이 음수/0/양수 신호를 정확히 만들어줘.
예시
const arr = [3, 1, 2];
arr.sort((a, b) => a - b);
정렬기는 내부적으로 두 개씩 꺼내 이렇게 판단해(정확한 순서는 엔진마다 달라도 “두 개 비교해서 앞/뒤” 원리는 동일):
- 3과 1 비교 → 3-1=2(양수) → 3이 뒤
- 1과 2 비교 → 1-2=-1(음수) → 1이 앞
- 3과 2 비교 → 3-2=1(양수) → 3이 뒤
이런 식으로 반복되면서 결과는 [1, 2, 3].
자주 헷갈리는 포인트
- 비교 함수가 true/false를 반환하면 안 됨
숫자를 반환해야 해. (a > b)만 반환하면 1/0만 나와서 “같다(0)”와 “작다(음수)”를 구분 못 해. - 비교 함수를 안 주면 문자열로 바꿔 사전식으로 정렬해버림.
숫자 배열은 반드시 (a, b) => a - b 써. - sort는 원본 배열을 바꾼다.
원본 보존하려면 arr.toSorted(compareFn)(ES2023+) 또는 arr.slice().sort(compareFn).
미니 가이드
- “그냥 숫자 오름차순” → arr.sort((a,b)=>a-b)
- “숫자 내림차순” → arr.sort((a,b)=>b-a)
- “사람 눈에 자연스러운 문자열(한글/대소문자 무시/숫자 인식)” →
arr.sort((a,b)=>a.localeCompare(b,"ko",{numeric:true,sensitivity:"base"})) - “간단 사전식 비교 한 줄” → arr.sort((a,b)=>(a>b)-(a<b))