[week02/03.23]TIL

CHO WanGi·2025년 3월 23일

KRAFTON JUNGLE 8th

목록 보기
11/89

오늘 한줄 요약

파이썬만 하다가 JS하니까 왜... 재밌지?

오늘 공부한 것

  • JS로 파이썬으로 풀었던 1주차 문제 다시 풀어보기

새로 배우게 된 것

  • JavaScript로 백준 풀기

입력받기

fs 모듈과 readline 모듈

  • fs 모듈
    • 백준에서 저 파일 경로를 다른 경로로 넣게 되면 ENOENT 에러가 발생하니 주의할 것!
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split(" ");
  • readline 모듈
/* readline Module */

// 문제 풀이
function solution(input) {
	
	// 답변 출력
	console.log(input);

}



/* readline Module */
const readline = require("readline");
const rl = readline.createInterface({

	input: process.stdin,
    output: process.stdout,

});

 
let input;

rl.on("line", function (line) {
    
    input = line;           // 입력받은 문자열, line
    input = parseInt(line); // 형변환, parseInt
    rl.close();             // 입력 종료

}).on("close", function () {

    solution(input); // 문제 풀이 함수 호출
    process.exit();  // 프로세스 종료

});

백준 문제를 풀이할 때는 더 간편한 fs 모듈을 이용하여 풀고 있지만,
회사별 코테 플랫폼마다 JS 입출력 방식이 다르기 때문에 미리 확인할 것.

문자열을 배열로 만드는 방법

let example = 'hello'

example.split('')
let arr = [...example]
let arr = Array.from(example)

문자열 음수 인덱스 활용: at()

let example = "hello";
console.log(example.at(-1)); // 출력: 'o'
console.log(example.at(-2)); // 출력: 'l'

혹은 위의 방법으로 리스트로 변경 후 arr.length - 1 로 접근해도 된다.

switch문은 조건식 평가가 불가능

❌ 아래 코드는 100000000% 틀린 코드 ❌


switch (score) {
  case score >= 90:
    console.log("A")
    break
  case score >= 80:
    console.log("B")
    break
  case score >= 70:
    console.log("C")
    break
  case score >= 60:
    console.log("D")
    break
  default:
    console.log("F")
    break
}

case score = 90: 처럼 정확한 값을 주어야 한다.
이런경우는 switch가 아닌 if~else 문으로 작성해주어야 함.

if (score >= 90) {
  console.log("A")
} else if (score >= 80) {
  console.log("B")
} else if (score >= 70) {
  console.log("C")
} else if (score >= 60) {
  console.log("D")
} else {
  console.log("F")
}

길이를 지정하여 배열 초기화 및 생성하기

JS에서 배열을 만드는 것은 []템플릿을 활용하면 쉽다.
다만, 알고리즘 문제를 풀면서 모든 인덱스의 값을 0으로 초기화하고
해당 인덱스의 해당하는 값에 1씩 더하면서
무언가를 카운트 할때는 템플릿 보단 아래의 방법으로 생성하는게 좋다.

answer_array = new Array(10).fill(0) 

위 처럼 안하고 템플릿에 접근하면 undefined + 1 이되어 NaN이 되기 때문.

배열의 요소 순회 : for...of, forEach

  • for...of 문 => 파이썬의 for in 문과 정확히 동일한 역할을 한다.
    • JS에서의 for in 은 값이 아닌 인덱스를 반환하기에 주의 할 것
  • forEach => 배열 요소 각각에 대해 제공한 콜백함수를 실행하게 된다.
for (let cases of arr) {
  let A = cases[0]
  let B = cases[1]
  console.log(A + B)
}
arr.forEach(pair => {
   let A = pair[0]
   let B = pair[1]
   console.log(A + B)
})

배열의 요소의 총합 구하기 : forEach❌ vs reduce✅

  • forEach
    • 배열의 각 요소를 순차적으로 실행(즉, 요소를 하나씩 다룰때)
    • 리턴 값이 없다
    • 단순 반복작업에 사용
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(num => console.log(num));  // 1 2 3 4 5
  • reduce
  • 배열을 하나의 값으로 누적시 사용
  • 반드시 리턴값이 존재
  • 누적값을 업데이트 하면서 결과값 생성
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum);  // 출력: 15

배열에 요소 추가 :push

JS에선 append 아니고 push!

result = []

for (let elems of arr) {
  if (elems < X) {
    result.push(elems)
  }
}

배열에서 최대값 뽑아내기

전개 연산자로 배열을 풀어준 다음에 Math.max 메서드를 활용

Math.max(...arr);

배열 요소의 인덱스 리턴 : find(), indexOf(), findIndex()

  • indexOf()
    • 배열에서 특정 요소의 인덱스를 리턴한다
    • 반환 타입은 number 이며 없다면 -1
  • find()
    • 판별 함수를 만족하는 첫 요소를 반환
    • 원하는 요소를 찾으면 검색을 중단하고 해당 요소를 반환한다
    • 없다면 undefined
  • findIndex()
    • 판별 함수를 만족하는 첫 요소를 반환
    • 원하는 요소를 찾자마자 메서드 종료
    • 없다면 - 1
const people = [
    { name: "Alice", age: 25 },
    { name: "Bob", age: 30 },
    { name: "Charlie", age: 35 },
    { name: "David", age: 40 }
];

const names = ["Alice", "Bob", "Charlie", "David"];

// indexOf
const index = names.indexOf("Charlie");
console.log(index); // 출력: 2


// find
const foundPerson = people.find(person => person.name === "Charlie");

console.log(foundPerson); // 출력: { name: "Charlie", age: 35 }


// findIndex
const indexOfPerson = people.findIndex(person => person.age === 35);

console.log(indexOfPerson); // 출력: 2

배열 합치고 자르기 : concat() , slice()

  • concat()
    두 개의 배열을 하나로 만들때 + 연산자가 아니고 concat을 활용해야 한다.

  • slice(start, end)
    어떤 배열을 start 부터 end -1 까지 자른다(파이썬의 range 와 범위가 똑같다)
    단, 원본을 대체하지 않고, 새로운 배열로 복사한다.

  // ✅
  merged_arr = merged_arr.concat(low_arr.slice(l)).concat(high_arr.slice(h));
// ❌
  merged_arr += low_arr.slice(l,)
  merged_arr += high_arr.slice(h,)

배열의 원소 제거 : filter() , splice()

  • filter(callbackFn)
    배열의 요소 하나씩 돌면서 판별 함수에 따라서 배열의 요소를 제거한다
const words = ["spray", "elite", "exuberant", "destruction", "present"];
// 문자열 길이가 6 미만인 것 제거
const result = words.filter((word) => word.length > 6);

console.log(result);
// Expected output: Array ["exuberant", "destruction", "present"]
  • splice(start[, deleteCount[, item1[, item2[, ...]]]])
    배열에서 요소 제거 ,추가 둘다 가능한 메서드인데, 원본배열의 값을 바꾼다는 것에 유의!
var myFish = ["angel", "clown", "drum", "mandarin", "sturgeon"];
var removed = myFish.splice(3, 1); //["angel", "clown", "drum", "sturgeon"]

문자열 반복 메서드 : repeat()

JS는 파이썬처럼 문자열에 숫자를 곱한다고 문자열 반복이 발생하지 않는다.
아래와 같이 repeat() 메서드를 활용해야 한다.

  console.log("*".repeat(i))

문자열, 정수형 등 아스키 코드로 변경 : charCodeAt()

console.log(input.charCodeAt())

문자열 길이 순 정렬

숫자 정렬하듯이 문자열의 길이를 넘겨주면 된다.

string_list.sort((prev, cur) => prev.length - cur.length)

출력이 많다면 result 배열에 모아서 출력하기

  • console.log 활용 코드
    아래 코드는 계속 시간초과가 나서 확인해보니 출력할 값이 많아지면 console.log가
    느리기 때문에 시간 초과가 날 수 있다고 한다
const fs = require('fs')
const input = fs.readFileSync('/dev/stdin').toString().trim()

let N = parseInt(input)

function hanoi(N, start, end, sub) {
  if (N == 1) {
    console.log(start, end)
  } else {
    hanoi(N - 1, start, sub, end)
    console.log(start, end)
    hanoi(N - 1, sub, end, start)
  }
}

console.log((BigInt(2) ** BigInt(N) - BigInt(1)).toString());

if (N <= 20) {
  hanoi(N, 1, 3, 2)
}
  • 개선
// 하노이탑
// hanoi(3) => 2개의 원판 A -> B (C 경유)
// 3번째 원판 A -> C 
// 나머지 2개의 원판 B -> C (A 경유)

const fs = require('fs')
const input = fs.readFileSync('input.txt').toString().trim()

let N = parseInt(input)

// console.log는 시간 초과가 날 수 있다
let result = []; // 결과 출력용 배열

function hanoi(N, start, end, sub) {
  if (N == 1) {
    result.push(`${start} ${end}`)
  } else {
    hanoi(N - 1, start, sub, end)
    result.push(`${start} ${end}`)
    hanoi(N - 1, sub, end, start)
  }
}

console.log((BigInt(2) ** BigInt(N) - BigInt(1)).toString());

if (N <= 20) {
  hanoi(N, 1, 3, 2);
  console.log(result.join('\n'))
}

정렬 메서드 : sort()

  • sort()
    파이썬은 문자, 숫자 가리지 않고 정렬해주지만 우리의 JS는 그렇지 않다.
    아무런 인자 없이 sort()만 사용하게 되면 문자열을 기준으로 정렬하게 된다.

  • 오름차순으로 숫자를 정렬하려면?

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

위와 같이 해야 숫자가 오름차순으로 정렬되며,b - a를 하게 되면 내림차순으로 큰 수부터 앞에 온다.

JS로 구현한 순열과 조합

JS에는 Itertools 처럼 모듈이 없기 때문에 직접 한땀한땀 구현해야한다.

순열



const getPerm = (arr, n) => {
  // 하나만 뽑는 경우 원소 하나씩 리턴
  if (n == 1) return arr.map(el => [el]);
  const result = [];
	// 입력으로 받은 배열을 반복문 돌리기
	// fixed : 고정된 부분, 이 고정된 부분을 제외하고 나머지 모든 배열이 순열 대상
	// 해당 원소를 다시 포함시켜 순서가 다른 경우도 고려
  arr.forEach((fixed, idx, origin) => {
    // Fixed 부분을 제외한 나머지 원소들로 배열 다시 제작
    const rest = [...origin.slice(0, idx), ...origin.slice(idx + 1)]
    // 순열 재귀적으로 돌리기
    const perms = getPerm(rest, n - 1)
    // 각각 만들어진 순열 앞에 고정된 부분 붙이기
    const attached = perms.map(perm => [fixed, ...perm])
    // 결과 값 모아두는 배열에 푸시
    result.push(...attached)
  });
  return result
}

조합


const getCombination = (arr, n) => {

  if (n === 1) return arr.map(el => [el]);
  // 최종 결과를 담을 배열
  const result = [];

  // fixed : 고정할 원소, origin : 원본 배열
  arr.forEach((fixed, idx, origin) => {
    // 현재 고정한 원소 이후의 배열을 나머지로 선언
    const rest = origin.slice(idx + 1);
    // 나머지와 n-1을 다시 전달하여 재귀호출
    const combis = getCombination(rest, n - 1);
    // 재귀호출이 끝나고 돌아오는 시점은
    // 처음 고정한 fixed로 구성 가능한 모든 조합을 리턴받은 이후
    // 따라서 리턴받은 값과 fixed를 다시 합쳐주어 조합 구성
    const attached = combis.map(combi => [fixed, ...combi]);
    // 구성된 조합 배열을 결과에 푸시
    result.push(...attached);
  });

  // 위에서 모든 재귀호출이 종료되면 저장된 조합 경우의 수 리턴
  return result;
}

공부하면서 아쉬운 점

파이썬에서 모듈을 통해 쉽게 구현했던 것을
직접 구현할 때 조금 어려움을 겪었다, 추후 C언어 활용시 정렬도 다 구현해야하는데...
기본 원리를 좀 더 이해하는 노력이 필요하다.

profile
제 Velog에 오신 모든 분들이 작더라도 인사이트를 얻어가셨으면 좋겠습니다 :)

0개의 댓글