在 JavaScript 中将长数字转换为缩写字符串,有特殊的简短要求

IT技术 javascript numbers
2021-03-13 14:16:42

在 JavaScript 中,如何编写一个函数将给定的 [edit: positive integer ] 数字(低于 1000 亿)转换为 3 个字母的缩写——其中 0-9 和 z/AZ 算作一个字母,但点(因为它在许多比例字体中非常小)不会,并且会在字母限制方面被忽略?

这个问题与这个有用的线程有关,但不一样;例如,该函数将变成例如“123456 -> 1.23k”(“123.5k”是 5 个字母)我正在寻找可以“123456 -> 0.1m”(“0[.]1m”是 3 个字母)的东西)。例如,这将是希望函数的输出(左原始,右理想返回值):

0                      "0"
12                    "12"
123                  "123"
1234                "1.2k"
12345                "12k"
123456              "0.1m"
1234567             "1.2m"
12345678             "12m"
123456789           "0.1b"
1234567899          "1.2b"
12345678999          "12b"

谢谢!

更新:谢谢!当进行以下修改时,答案在并按照要求工作:

function abbreviateNumber(value) {
    var newValue = value;
    if (value >= 1000) {
        var suffixes = ["", "k", "m", "b","t"];
        var suffixNum = Math.floor( (""+value).length/3 );
        var shortValue = '';
        for (var precision = 2; precision >= 1; precision--) {
            shortValue = parseFloat( (suffixNum != 0 ? (value / Math.pow(1000,suffixNum) ) : value).toPrecision(precision));
            var dotLessShortValue = (shortValue + '').replace(/[^a-zA-Z 0-9]+/g,'');
            if (dotLessShortValue.length <= 2) { break; }
        }
        if (shortValue % 1 != 0)  shortValue = shortValue.toFixed(1);
        newValue = shortValue+suffixes[suffixNum];
    }
    return newValue;
}
6个回答

我相信 ninjagecko 的解决方案并不完全符合您想要的标准。以下功能执行:

function intToString (value) {
    var suffixes = ["", "k", "m", "b","t"];
    var suffixNum = Math.floor((""+value).length/3);
    var shortValue = parseFloat((suffixNum != 0 ? (value / Math.pow(1000,suffixNum)) : value).toPrecision(2));
    if (shortValue % 1 != 0) {
        shortValue = shortValue.toFixed(1);
    }
    return shortValue+suffixes[suffixNum];
}

对于大于 99 万亿的值,不会添加字母,可以通过附加到“后缀”数组轻松修复。

Philipp 的编辑如下:通过以下更改,它完全符合所有要求!

function abbreviateNumber(value) {
    var newValue = value;
    if (value >= 1000) {
        var suffixes = ["", "k", "m", "b","t"];
        var suffixNum = Math.floor( (""+value).length/3 );
        var shortValue = '';
        for (var precision = 2; precision >= 1; precision--) {
            shortValue = parseFloat( (suffixNum != 0 ? (value / Math.pow(1000,suffixNum) ) : value).toPrecision(precision));
            var dotLessShortValue = (shortValue + '').replace(/[^a-zA-Z 0-9]+/g,'');
            if (dotLessShortValue.length <= 2) { break; }
        }
        if (shortValue % 1 != 0)  shortValue = shortValue.toFixed(1);
        newValue = shortValue+suffixes[suffixNum];
    }
    return newValue;
}
我只是对它做了一个小的调整(还有待同行评审)。我添加了 value = Math.round(value); 到函数的最开始。通过这样做,当“值”有小数位时它不会中断,例如。10025.26584 将变成 10k 而不是 NaN
2021-04-25 14:16:42
shortNum 最后在@PhilippLenssen 的编辑中没有定义 if (shortValue % 1 != 0) shortNum = shortValue.toFixed(1);
2021-05-05 14:16:42
谢谢!我做了一些修改,使其 100% 符合要求并编辑了您的答案(我希望这是在 StackOverflow 中执行此操作的正确方法,如果需要,请进行更改)。高超!
2021-05-10 14:16:42
只是出于好奇:我错过了哪个案例/要求?
2021-05-15 14:16:42
这些都是次要的,并且根据您的出色回答迅速进行了修改。以下测试数字略有遗漏:123 变为 0.12k(理想结果:123,因为这仍然是 3 个字母,因此可以);123456 变成了 0.12m(理想的:0.1m,因为 0.12m 是 4 个字母,减去点);与 0.12b 相同,经过修改现在变为 0.1b。再次感谢你的帮助!
2021-05-17 14:16:42

这也处理非常大的值,并且更加简洁和高效。

abbreviate_number = function(num, fixed) {
  if (num === null) { return null; } // terminate early
  if (num === 0) { return '0'; } // terminate early
  fixed = (!fixed || fixed < 0) ? 0 : fixed; // number of decimal places to show
  var b = (num).toPrecision(2).split("e"), // get power
      k = b.length === 1 ? 0 : Math.floor(Math.min(b[1].slice(1), 14) / 3), // floor at decimals, ceiling at trillions
      c = k < 1 ? num.toFixed(0 + fixed) : (num / Math.pow(10, k * 3) ).toFixed(1 + fixed), // divide by power
      d = c < 0 ? c : Math.abs(c), // enforce -0 is 0
      e = d + ['', 'K', 'M', 'B', 'T'][k]; // append power
  return e;
}

结果:

for(var a='', i=0; i < 14; i++){ 
    a += i; 
    console.log(a, abbreviate_number(parseInt(a),0)); 
    console.log(-a, abbreviate_number(parseInt(-a),0)); 
}

0 0
-0 0
01 1
-1 -1
012 12
-12 -12
0123 123
-123 -123
01234 1.2K
-1234 -1.2K
012345 12.3K
-12345 -12.3K
0123456 123.5K
-123456 -123.5K
01234567 1.2M
-1234567 -1.2M
012345678 12.3M
-12345678 -12.3M
0123456789 123.5M
-123456789 -123.5M
012345678910 12.3B
-12345678910 -12.3B
01234567891011 1.2T
-1234567891011 -1.2T
0123456789101112 123.5T
-123456789101112 -123.5T
012345678910111213 12345.7T
-12345678910111212 -12345.7T
我喜欢这种方法,因为它比其他答案更好地处理非整数。
2021-04-22 14:16:42
我最近在 Python 中遇到了同样的问题,并很快想出了一个更简单的方法(我想我的想法在过去 5 年里发生了变化)。如果它有帮助,就在这里;我很想在 JS 中看到它。stackoverflow.com/a/65084005/1438550
2021-04-25 14:16:42
对于非常小的值(例如5e-18),这将返回“0T”,因此我添加了以下内容:if (num < 1e-3) { return '~0'; }
2021-05-08 14:16:42
更好的解决方案
2021-05-17 14:16:42

方法一:内置库

我建议使用 Javascript 的内置库方法Intl.NumberFormat

Intl.NumberFormat('en-US', {
  notation: "compact",
  maximumFractionDigits: 1
}).format(2500);

方法二:没有图书馆

但是您也可以使用简单的if语句创建这些缩写,而无需复杂的Math, maps, regex, for-loops 等。

用 K 格式化现金value

const formatCash = n => {
  if (n < 1e3) return n;
  if (n >= 1e3) return +(n / 1e3).toFixed(1) + "K";
};

console.log(formatCash(2500));

用 KMBT 格式化现金value

const formatCash = n => {
  if (n < 1e3) return n;
  if (n >= 1e3 && n < 1e6) return +(n / 1e3).toFixed(1) + "K";
  if (n >= 1e6 && n < 1e9) return +(n / 1e6).toFixed(1) + "M";
  if (n >= 1e9 && n < 1e12) return +(n / 1e9).toFixed(1) + "B";
  if (n >= 1e12) return +(n / 1e12).toFixed(1) + "T";
};

console.log(formatCash(1235000));

使用负数

let format;
const number = -1235000;

if (number < 0) {
  format = '-' + formatCash(-1 * number);
} else {
  format = formatCash(number);
}

这是我认为相当优雅的解决方案。它不会尝试处理负数

const COUNT_ABBRS = [ '', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ];

function formatCount(count, withAbbr = false, decimals = 2) {
    const i     = 0 === count ? count : Math.floor(Math.log(count) / Math.log(1000));
    let result  = parseFloat((count / Math.pow(1000, i)).toFixed(decimals));
    if(withAbbr) {
        result += `${COUNT_ABBRS[i]}`; 
    }
    return result;
}

例子

   formatCount(1000, true);
=> '1k'
   formatCount(100, true);
=> '100'
   formatCount(10000, true);
=> '10k'
   formatCount(10241, true);
=> '10.24k'
   formatCount(10241, true, 0);
=> '10k'
   formatCount(10241, true, 1)
=> '10.2k'
   formatCount(1024111, true, 1)
=> '1M'
   formatCount(1024111, true, 2)
=> '1.02M'
这并不能解决提出的问题,但这正是正在寻找的。谢谢。
2021-05-05 14:16:42
我比 k、m、b 更喜欢 SI 符号。1b 在美国/英国是 1e9,但在许多其他国家是 1e12。
2021-05-16 14:16:42

现代、简单、内置、高度可定制和“无代码”的方式:Intl.FormatNumber格式函数(兼容性图

var numbers = [98721, 9812730,37462,29,093484620123, 9732,0283737718234712]
for(let num of numbers){
  console.log(new Intl.NumberFormat( 'en-US', { maximumFractionDigits: 1,notation: "compact" , compactDisplay: "short" }).format(num));
}
98.7K
9.8M
37.5K
29
93.5B
9.7K
283.7T

笔记: