I've been working on numerals conversion lately. Now the JavaScript course I'm following asked me to do something similar for Roman numerals.To keep things fresh and given the amount of test-cases provided I decided to write this one Test-Driven Development style. That is, for as much as I understand it.First I check whether it's dividable by a full numeral (M, C, etc.). After that I have to reverse the string because the numerals were added in reverse order. The next step is to translate all combinations into their correct form using regex and a translation map. The functions were written in that order and fixing the problem the last part caused.So far it looks very messy. Is this how one is supposed to solve problems TDD style? There is definitely a lot of code duplication going on, could this be fixed with another map?ChallengeConvert the given number into a Roman numeral.All Roman numerals answers should be provided in upper-case.Test casesconvert(5) // V.convert(9) // IX.convert(12) // XII.convert(16) // XVI.convert(29) // XXIX.convert(44) // XLIV.convert(45) // XLVconvert(68) // LXVIIIconvert(83) // LXXXIIIconvert(97) // XCVIIconvert(99) // XCIXconvert(500) // Dconvert(501) // DIconvert(649) // DCXLIXconvert(798) // DCCXCVIIIconvert(891) // DCCCXCIconvert(1000) // Mconvert(1004) // MIVconvert(1006) // MVIconvert(1023) // MXXIIIconvert(2014) // MMXIVconvert(3999) // MMMCMXCIXCodefunction convert(num) {  roman = ;  for (var i = 0; i < num; i++) {    //MDCLXVI    if (!(num % 1000)) { roman += M; num -= 1000; }    else if (!(num % 500)) { roman += D; num -= 500; }    else if (!(num % 100)) { roman += C; num -= 100; }    else if (!(num % 50)) { roman += L; num -= 50; }    else if (!(num % 10)) { roman += X; num -= 10; }    else if (!(num % 5)) { roman += V; num -= 5; }    else if (!(num % 1)){ roman += I; num -= 1; }  }  roman = roman.split('').reverse().join('');  var translationMap = {    DCCCC : 'CM',    CCCC : 'CD',    LXXXX : 'XC',    XXXX : 'XL',    VIIII : 'IX',    IIII : 'IV',  };  for (var i in translationMap) {    roman = roman.replace(new RegExp(i,'g'), translationMap[i]);  }  return roman;}convert(3999);Run it at repl.it
Number to Roman numerals
javascript;unit testing;regex;roman numerals
@SirPython has already given us some great steps to improve this implementation but I'd like to focus on the process of getting there as well since that seems to be part of the question.Is this how one is supposed to solve problems TDD style?I think it's interesting that you suggest TDD shapes your solution. I find that a test driven approach changes my interfaces but has much less impact on what the final implementation of a function looks like. Whatever process you use, if you end up with very messy code then I think something needs to change. Code is written for humans to read and messy code will cause you grief in the future.I'm not sure what your workflow looked like to arrive at this solution but here's what I would expect:Write a test describing what the unit you are testing should do.See the test fail.Write code to pass the test.See the test pass.Consider if a refactor could improve your implementation and if so apply it.See that all the tests still pass, giving you confidence that your refactor was a safe change.You have a bunch of test cases but running through and verifying them manually seems tedious. Let's start by making that easy. There are a bunch of testing libraries you might use but we can practice TDD with something simple:function runTests(tests) {    var failureCount = 0    var testsCount = tests.length    var testTimerName = 'tests completed in'    console.time(testTimerName)    for (var t = 0; t < testsCount; t++) {        var test = tests[t]        var input = test['input']        var expectation = test['expected']        var output = convert(input)        if (output != expectation) {            console.log('Failure: expected convert(' + input + ') to equal \'' + expectation + '\' but got \'' + output + '\'')            failureCount += 1        }    }    console.log(testsCount - failureCount + '/' + testsCount + ' tests passed.')    console.timeEnd(testTimerName)}var tests = [    {'input': 5, 'expected': 'V'},    ]runTests(tests)When run we get:ReferenceError: convert is not definedWe need to at least implement a convert function:function convert(num) {    return ''}>Failure: expected convert(5) to equal 'V' but got ''0/1 tests passed.tests completed in: 1msA test failed. That's good, we know we'll see failures when they happen. Let's make the test pass.function convert(num) {    return 'V'}>1/1 tests passed.tests completed in: 1msNow we have a quick way to run our tests every time we make a change and see which, if any, failed. That should allow us to make changes to convert fearlessly, confident that we will know if we're improving the implementation or if we break behaviors which used to work.From here we could add one test case at a time, update convert to make the new test pass, clean up our work, and then repeat. Sometimes that sort of incremental approach works great. Alternately we might write a bunch of tests, all of which will fail for now, and then work on getting more and more of them to pass. Since we already have some idea what our implementation might look like let's take that second approach.var tests = [    {'input': 1, 'expected': 'I'},    {'input': 2, 'expected': 'II'},    {'input': 3, 'expected': 'III'},    {'input': 4, 'expected': 'IV'},    {'input': 5, 'expected': 'V'},    {'input': 9, 'expected': 'IX'},    {'input': 12, 'expected': 'XII'},    {'input': 16, 'expected': 'XVI'},    {'input': 29, 'expected': 'XXIX'},    {'input': 44, 'expected': 'XLIV'},    {'input': 45, 'expected': 'XLV'},    {'input': 68, 'expected': 'LXVIII'},    {'input': 83, 'expected': 'LXXXIII'},    {'input': 97, 'expected': 'XCVII'},    {'input': 99, 'expected': 'XCIX'},    {'input': 500, 'expected': 'D'},    {'input': 501, 'expected': 'DI'},    {'input': 649, 'expected': 'DCXLIX'},    {'input': 798, 'expected': 'DCCXCVIII'},    {'input': 891, 'expected': 'DCCCXCI'},    {'input': 1000, 'expected': 'M'},    {'input': 1004, 'expected': 'MIV'},    {'input': 1006, 'expected': 'MVI'},    {'input': 1023, 'expected': 'MXXIII'},    {'input': 2014, 'expected': 'MMXIV'},    {'input': 3999, 'expected': 'MMMCMXCIX'},    ]runTests(tests)>Failure: expected convert(2) to equal 'II' but got 'I'Failure: expected convert(3) to equal 'III' but got 'II'Failure: expected convert(4) to equal 'IV' but got 'II'23/26 tests passed.tests completed in: 3msInteresting, looks like there are at least a few cases this implementation does not handle correctly yet. We can fix these and running our tests as we go will make sure we don't break other test cases (like 'IX') as we make changes.In addition to helping us implement new behavior these tests are also useful for the final and essential refactor step in a TDD red-green-refactor workflow. Now that we have test verifying that this function behaves as we expect we can step back and consider how the implementation can be improved with confidence that we haven't changed its behavior, as long as our tests still pass. If we apply the changes @SirPython suggested we will hopefully see that our tests still pass and perhaps even run in less time.Taking time for this final refactor step is, I think, essential for making sure incrementally developed behaviors do not end up as the sort of messy but probably valid implementation @Mast wanted to avoid.