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

이진선·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개의 댓글