You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何两款仅键值映射不同的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 16:42:52