자바스크립트로 코딩테스트 준비하기

이진선·2020년 1월 1일
2

들어가기전에

이번에 미* 라는 스타트업에서 코딩테스트를 떨어졌다!
부들부들 하지만, 내가 낸 코드 결과물을 보니 그럴만도 하다.

코딩테스트는 못하는 사람들을 걸러내는 시험이라고 생각한다. 어렵고 복잡한 문제를 내서 구직자를 곤란하게 하는것이 아니라, 평소에 이 사람이 어떤식으로 코드를 짜고 테스트하고 관리하는지, 이해는 잘 되는지, 클린한지,
나라면 그런걸 검토할 것이다.

단순히 코딩테스트로 역량을 알아내는건 한계가 있지만, 코드짜는 스타일은 닦아온 그대로 보여줄 수 밖에 없지 않을까

다음 코테에서는 이렇게 생각없이 문제풀이위주로 짜지 않으려고 검토할겸 기록을 남긴다.

두문제 다섯시간, 난이도 쉬움을 왜 다 풀지도 못했을까 싶지만, 반성할겸 코드 스타일 점검할겸 복기해보자

  1. 매우 큰 정수 두개를 더하는 CLI 프로그램짜기.
    Bigint 관련 라이브러리 사용은 금지,
    인풋을 그대로 형변환 금지,
  2. 한글로 입력된 매우 큰 정수 두개를 더하는 CLI 프로그램짜기

처음 풀때는 CLI 프로그램을 Stdin 로 인풋받는 프로그램인줄 알고 readline 라이브러리를 사용해서 테스트 환경을 구축했다.

const question = function () {
	rl.question('give me a number1: ', (val1) => {
        rl.question('give me a number2: ', (val2) => {
            const result = sum_Of_BigInt(translate(val1), translate(val2));
            console.log(toHan(result))

            rl.close()
        })
    })
}

그런데 이게 절대 아닌거같다.
커맨드로 실행할 수 만 있으면 되는거 아닐까?

두번 풀때는 함수와, 테스트케이스를 받아 테스트를 돌리는 util 함수를 사용했다.

exports.runTest = (f, testCase) => {
  time("time");
  const testResult = testCase.map((tc, index) => {
    const result = { index, ...tc };
    const { args, target } = result;
    try {
      const expect = f(...args);
      result.expect = expect;
      result.ok = compareAnswer(expect, target);
    } catch (error) {
      result.error = error;
    }

    return result;
  });
  timeEnd("time");

  testResult.map(makeResultString).forEach(str => log(str));
  log(analyzeTestResult(testResult));
};

다른분에 코드를 상당부분 가져왔다. 나중에는 내 입맛에 맞게 adapt 해야할것같다.

문제로 들어가보자

1번은, 큰 정수를 문자열로 입력을 받아 더한 후 다시 문자열로 반환해주어야한다.
어떻게 풀어야 할까

  • 문자열을 한개씩 쪼개서 배열에 넣고, 두 배열에 자릿수를 맞춰서 더한 후 배열을 쪼인해서 리턴
  1. 문자열을 배열로 바꾸는 함수
  2. 배열을 자릿수맞춰서 더하는 함수

두가지 함수를 만들어서 풀자.

처음 푼 코드는 아래와 같다.

function BigInt(string) {
    const result = []
    if (!string) result.push('0')
    for (let i = 0; i < string.length; i++) {
        result.push(string[i])
    }
    // console.log(result);

    return result
}
function sum_Of_BigInt(a, b) {
    const val1 = new BigInt(a)
    const val2 = new BigInt(b)
    const result = []
    const diff = val1.length - val2.length

    if (diff < 0) {
        for (let i = 0; i < diff * -1; i++) {
            val1.unshift('0')
        }
    }
    else {
        for (let i = 0; i < diff; i++) {
            val2.unshift('0')
        }
    }

    let up = 0
    for (let i = val1.length - 1; i >= 0; i--) {
        let v1 = val1[i] * 1
        let v2 = val2[i] * 1
        // result.unshift(v1 + v2)
        let temp = v1 + v2 + up
        if (temp >= 10) {
            result.unshift(temp - 10);
            up = 1
        }
        else {
            result.unshift(temp)
            up = 0
        }
    }
    if (up) result.unshift(up)

    // console.log(result)
    // console.log('answer : ', result.join(''))
    return (result.join(''))
}

352 를 넣으면 ["3", "5", "2"] 가 되고
21 을 넣으면 ["2", "1"] 가 된다.

두 수를 더할때 ["2", "1"]을 ["0", "2", "1"] 로 자릿수를 맞춰주고 마지막부터 같은 줄끼리 더하고 올림값이 있으면 추가로 더해주는 식으로 구현했다.

구현자체가 틀리지는 않았지만,
깔끔하게 바꾸려면 어떻게 해야할까...

람다식을 최대한 활용하고, If문을 최대한 줄이려고 했다.

그 결과 이렇게 바뀌었다.

const { log, error, time, timeEnd } = console;
const BigInt = (num: string): Array<string> => {

    const result: string[] = [];
    num.split('').forEach((val) => {
        result.unshift(val);
    })
    return result;
}

const ArrSum = (arr1: Array<string>, arr2: Array<string>): Array<string> => {
    if (arr1 === [] || arr2 === []) return arr1[0] && arr1 || arr2;

    const len = arr1.length > arr2.length && arr1.length || arr2.length;
    const result = [];

    let ceil = 0;
    for (let i = 0; i < len; i++) {
        let num1 = Number(arr1[i]) || 0;
        let num2 = Number(arr2[i]) || 0;

        let val = num1 + num2 + ceil;
        if (val >= 10) {
            val = val - 10;
            ceil = 1;
        }
        else {
            ceil = 0;
        }

        result.push(String(val));
    }
    if (ceil) result.push('1');
    result.reverse()

    log(result)
    return result;
}

다시 더 어떻게 개선할 수 있을까?

일단 두 함수는 무조건 독립적이어야 할것이다.
처음 풀때는 두번째함수에서 첫번째 함수를 사용하는 이런 부분은 무조건 빼줘야 할 것이고,

자릿수를 맞추는 부분을 BigInt로 옮기는것도 고려해봄직한가?
더할때 자릿수 맞추는 부분이 필요한거니까 이부분은 그대로 가야할까?

타입스크립트는 굳이 일단 필요가 없을것같아보이기도 한다.

const { log, error, time, timeEnd } = console;
const sum = (arg1, arg2) => {

    const StrtoArr = (str) => str.split('').reverse();
    const ArrSum = (arr1, arr2) => {

        const len = arr1.length > arr2.length && arr1.length || arr2.length;
        const result = [];

        let ceil = 0;
        for (let i = 0; i < len; i++) {
            let num1 = Number(arr1[i]) || 0;
            let num2 = Number(arr2[i]) || 0;

            let val = num1 + num2 + ceil;
            if (val >= 10) {
                val = val - 10;
                ceil = 1;
            }
            else {
                ceil = 0;
            }
            result.push(String(val));
        }
        if (ceil) result.unshift('1');
        return result.join('');

    }
    return ArrSum(arg1, arg2)
}

const runTest = (f, testCase) => {
    const result = testCase.map((ele, index) => {
        const { args, answer } = ele;
        const expect = f(...args);

        return ({ index, result: (answer === expect), answer, expect });
    })
    log(result)
}
const data = [
    [["123", "456"], "579"],
    [["99", "1"], "100"],
    [["1", "999"], "1000"],
]
const testCase = (data) => {
    return data.map((ele, index, arr) => {
        const args = ele[0];
        const answer = ele[1];
        return { args, answer };
    })
}
runTest(sum, testCase(data))

테스트하는 함수도 새로 작성해보고
Bigint 함수도 한줄로 줄였다.
또한 메인함수안에서 함수를 표현식으로 선언해서 사용했다.

ArrSum 함수내 for 문을 줄여보고 싶어서
map 함수로 해봤는데 아직은 잘안된다. 방법이 있을까 싶어진다..

0개의 댓글

관련 채용 정보