[JavaScript] [프로그래머스] 표 편집 : falsy 값에 유의하자!

falsy값 이란?

falsy 값이란 Boolean 문맥에서 false로 평가되는 값이다.

falsy 값 종류

falsy 값은 8가지가 있다.

  1. false
  2. 0 : 숫자 0
  3. -0 : 음수 0
  4. 0n : BigInt 0
  5. "" : 빈 string
  6. null : 아무런 값도 없음
  7. undefined
  8. NaN : 숫자가 아님

코테에서...

falsy 값에 대해서 알고는 있었는데, 실제로 주의를 기울여야한다는 것은 코딩테스트를 연습하면서 체감하게 되었다.
다음은 2021년 카카오 채용연계형 인턴십 문제이다.

2021 카카오 채용연계형 인턴십 - 표 편집

당시 코테 후기를 찾아보면 이 문제가 코테부문 당락을 갈랐다는 것을 알 수 있다.
(이 문제가 3번째 문제인데 4,5번째 문제는 각각 Lv.4, Lv.5 이다.)

처음엔 단순한 리스트로 문제를 풀었다가 효율성 부문에서 통과가 안되어서 리스트 내에 객체를 넣어서 양방향 리스트와 유사하게 작동하게끔 구현하였다.

const list = [];

for (let i = 0; i < n; i++) {
    if (i == 0)
        list[i] = {prev : null, next : i + 1, exist: true};
    else if (i === n - 1)
        list[i] = {prev : i - 1, next : null, exist: true};
    else
        list[i] = {prev : i - 1, next : i + 1, exist: true};
}

다음은 입력으로 들어오는 명령 각각에 대해서 수행하는 로직이다.

cmd.forEach((command) => {
    const [order, num] = command.split(' ');
    if (order === 'U') {
        let t = parseInt(num);
        while (t--) {
            cursor = list[cursor].prev;
        }
    } else if (order === 'D') {
        let t = parseInt(num);
        while (t--) {
            cursor = list[cursor].next;
        }
    } else if (order === 'C') {
        const {prev, next} = list[cursor];
		// 연결리스트 삭제와 유사 (실제로 배열에서 사라지지는 않음)
        if (prev) list[prev].next = next; 
        if (next) list[next].prev = prev;
        list[cursor].exist = false;
        deleted.push(cursor);
        cursor = next === null ? prev : next;
    } else if (order === 'Z') {
        const repaired = deleted.pop();
        const {prev, next} = list[repaired];
		// 삭제 전으로 돌아가게 만듬
        if (prev) list[prev].next = repaired;
        if (next) list[next].prev = repaired;
        list[repaired].exist = true;
    }
})

이 로직은 잘 돌아가는 것처럼 보이지만 테스트케이스 28번, 효율성케이스 8번에서 런타임 에러가 난다.

며칠을 고민하다가 친구(@dha)의 도움으로 찾을 수 있었는데,

...
if (prev) list[prev].next = next; // prev에 0이 들어올 수 있음
...
if (prev) list[prev].next = repaired;
...

null 을 방지하기 위해 넣은 조건문에 0이 들어오는 경우 조건문을 통과하지 못하기 때문이었다.
(prev, next에 저장되는 것은 인덱스이기 때문에 0이 들어올 수 있다.)

참으로 어이없는 실수다...

올바르게 고친 코드는 다음과 같다.

cmd.forEach((command) => {
    const [order, num] = command.split(' ');
    if (order === 'U') {
        let t = parseInt(num);
        while (t--) {
            cursor = list[cursor].prev;
        }
    } else if (order === 'D') {
        let t = parseInt(num);
        while (t--) {
            cursor = list[cursor].next;
        }
    } else if (order === 'C') {
        const {prev, next} = list[cursor];

        if (prev !== null) list[prev].next = next; // null 검사
        if (next !== null) list[next].prev = prev; // null 검사
        list[cursor].exist = false;
        deleted.push(cursor);
        cursor = next === null ? prev : next;
    } else if (order === 'Z') {
        const repaired = deleted.pop();
        const {prev, next} = list[repaired];

        if (prev !== null) list[prev].next = repaired; // null 검사
        if (next !== null) list[next].prev = repaired; // null 검사
        list[repaired].exist = true;
    }
})

마무리

fasly 값에 대해 처음 알게되었을때 단순하게 if 문을 쓸 때 주의해야지~ 라고만 생각하고 넘어갔었는데 이번 기회로 생각을 바꾸게 되었다.
단순히 코테를 연습할 때도 잘못 쓴 코드 하나를 잡느라 며칠이 걸렸는데, 실제 코딩테스트였다면? falsy 체크 제대로 못한 것 때문에 로직을 다 생각해놓고도 떨어졌을 것이다. 내가 언제든 바보같은 실수를 할 수 있다는 것을 인정하고 if 문을 쓸 때 정확하게 명시하는 습관을 가져야겠다.

좋은 웹페이지 즐겨찾기