为何两款仅键值映射不同的JS十进制转罗马数字代码在FreeCodeCamp测试结果迥异?
问题:十进制转罗马数字代码差异原因解析
为什么第一款以十进制数为键、罗马数字为值的JS代码无法通过测试,而第二款键值互换的代码却能正常工作?两款代码仅romanNumerals对象的键值映射不同,但第一款仅能处理2、3这类简单输入,对4、5、9等输入完全失效?
代码1(键为十进制数字)
function convertToRoman(num) { const romanNumerals = { 1000: "M", 900: "CM", 500: "D", 400: "CD", 100: "C", 90: "XC", 50: "L", 40: "XL", 10: "X", 9: "IX", 5: "V", 4: "IV", 1: "I" }; let roman = ""; let restartLoop = false; do { restartLoop = false; for (let key in romanNumerals) { if (num >= key) { roman += romanNumerals[key]; num -= key; restartLoop = true; break; } } } while (restartLoop); return roman; } console.log(convertToRoman(3)) console.log(convertToRoman(4)) console.log(convertToRoman(23))
代码2(键为罗马数字)
function convertToRoman(num) { const romanNumerals = { "M": 1000, "CM": 900, "D": 500, "CD": 400, "C": 100, "XC": 90, "L": 50, "XL": 40, "X": 10, "IX": 9, "V": 5, "IV": 4, "I": 1 }; let roman = ""; let restartLoop = false; do { restartLoop = false; for (let key in romanNumerals) { if (num >= romanNumerals[key]) { roman += key; num -= romanNumerals[key]; restartLoop = true; break; } } } while (restartLoop); return roman; } console.log(convertToRoman(3)) console.log(convertToRoman(4)) console.log(convertToRoman(23))
问题成因
核心原因是JavaScript对象的遍历顺序规则差异:
- 代码1的键是数字类型(或会被JS自动转换为数字的键),JS会对这类键进行升序排序,不管你定义时的顺序。也就是说,你写的是1000、900...1,但遍历的时候顺序会变成1、4、5、9、10...1000。这样循环会优先匹配最小的1,导致像4这样的数会生成
IIII而不是IV,完全跳过了4对应的映射规则。 - 代码2的键是罗马数字字符串(非数字可转换的字符串),ES6+规范中,这类键会按照定义时的顺序遍历,也就是从1000对应的
M开始,符合我们需要的「从大到小匹配罗马数字规则」的逻辑,所以能正确生成结果。
解决方法
方法1:改用数组存储映射关系(推荐)
数组的遍历顺序是完全可控的,把键值对按从大到小的顺序存在数组里,遍历数组即可避免顺序问题:
function convertToRoman(num) { const romanNumerals = [ [1000, "M"], [900, "CM"], [500, "D"], [400, "CD"], [100, "C"], [90, "XC"], [50, "L"], [40, "XL"], [10, "X"], [9, "IX"], [5, "V"], [4, "IV"], [1, "I"] ]; let roman = ""; for (const [value, symbol] of romanNumerals) { while (num >= value) { roman += symbol; num -= value; } } return roman; }
方法2:将数字键转为字符串
把代码1中的数字键改成字符串类型,这样JS会按照定义顺序遍历,逻辑和代码2一致:
function convertToRoman(num) { const romanNumerals = { "1000": "M", "900": "CM", "500": "D", "400": "CD", "100": "C", "90": "XC", "50": "L", "40": "XL", "10": "X", "9": "IX", "5": "V", "4": "IV", "1": "I" }; let roman = ""; let restartLoop = false; do { restartLoop = false; for (let key in romanNumerals) { const value = parseInt(key); if (num >= value) { roman += romanNumerals[key]; num -= value; restartLoop = true; break; } } } while (restartLoop); return roman; }
内容的提问来源于stack exchange,提问作者simLight
相关产品推荐
相关产品推荐

