在 JavaScript 中将长数字转换为缩写字符串,有特殊的简短要求
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10599933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert long number into abbreviated string in JavaScript, with a special shortness requirement
提问by Philipp Lenssen
In JavaScript, how would one write a function that converts a given [edit: positive integer] number (below 100 billion) into a 3-letter abbreviation -- where 0-9 and a-z/A-Z are counting as a letter, but the dot (as it's so tiny in many proportional fonts) would not, and would be ignored in terms of the letter limit?
在 JavaScript 中,如何编写一个函数将给定的 [edit: positive integer] 数字(低于 1000 亿)转换为 3 个字母的缩写——其中 0-9 和 z/AZ 算作一个字母,但点(因为它在许多比例字体中非常小)不会,并且会在字母限制方面被忽略?
This question is related to this helpful thread, but it's not the same; for instance, where that function would turn e.g. "123456 -> 1.23k" ("123.5k" being 5 letters) I am looking for something that does "123456 -> 0.1m" ("0[.]1m" being 3 letters). For instance, this would be the output of hoped function (left original, right ideal return value):
这个问题与这个有用的线程有关,但不一样;例如,该函数将变成例如“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"
Thanks!
谢谢!
Update: Thanks! An answer is in and works per the requirements when the following amendments are made:
更新:谢谢!当进行以下修改时,答案就在并按照要求工作:
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;
}
采纳答案by chucktator
I believe ninjagecko's solution doesn't quite conform with the standard you wanted. The following function does:
我相信 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];
}
For values greater than 99 trillion no letter will be added, which can be easily fixed by appending to the 'suffixes' array.
对于大于 99 万亿的值,不会添加字母,可以通过附加到“后缀”数组轻松修复。
Edit by Philipp follows:With the following changes it fits with all requirements perfectly!
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;
}
回答by D.Deriso
This handles very large values as well and is a bit more succinct and efficient.
这也处理非常大的值,并且更加简洁和高效。
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;
}
Results:
结果:
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
回答by tfmontague
A lot of answers on this thread get rather complicated, using Math
objects, map objects, for-loops, etc. But those approaches don't actually improve the design very much - they introduce more lines of code, more complexity, and more memory overhead. After evaluating several approaches, I think the manual approach is the easiest to understand, and provides the highest performance.
这个线程上的很多答案都变得相当复杂,使用Math
对象、映射对象、for 循环等。但这些方法实际上并没有很大地改进设计——它们引入了更多的代码行、更多的复杂性和更多的内存开销. 在评估了几种方法后,我认为手动方法最容易理解,并且性能最高。
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));
回答by HoangLong85
I think you cant try this numeraljs/
If you want convert 1000 to 1k
我想你不能试试这个numericjs/
如果你想把 1000 转换成 1k
console.log(numeral(1000).format('0a'));
and if you want convert 123400 to 123.4k try this
如果你想将 123400 转换为 123.4k 试试这个
console.log(numeral(123400).format('0.0a'));
回答by NuSkooler
Here's what I think is a fairly elegant solution. It does not attempt to deal with negative numbers:
这是我认为相当优雅的解决方案。它不会尝试处理负数:
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;
}
Examples:
例子:
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'
回答by ninjagecko
Based on my answer at https://stackoverflow.com/a/10600491/711085, your answer is actually slightly shorter to implement, by using .substring(0,3)
:
根据我在https://stackoverflow.com/a/10600491/711085上的回答,您的回答实际上要短一些,可以使用.substring(0,3)
:
function format(n) {
with (Math) {
var base = floor(log(abs(n))/log(1000));
var suffix = 'kmb'[base-1];
return suffix ? String(n/pow(1000,base)).substring(0,3)+suffix : ''+n;
}
}
(As usual, don't use Math unless you know exactly what you're doing; assigning var pow=...
and the like would cause insane bugs. See link for a safer way to do this.)
(像往常一样,除非您确切地知道自己在做什么,否则不要使用 Math;分配var pow=...
等会导致疯狂的错误。有关更安全的方法,请参阅链接。)
> tests = [-1001, -1, 0, 1, 2.5, 999, 1234,
1234.5, 1000001, Math.pow(10,9), Math.pow(10,12)]
> tests.forEach(function(x){ console.log(x,format(x)) })
-1001 "-1.k"
-1 "-1"
0 "0"
1 "1"
2.5 "2.5"
999 "999"
1234 "1.2k"
1234.5 "1.2k"
1000001 "1.0m"
1000000000 "1b"
1000000000000 "1000000000000"
You will need to catch the case where the result is >=1 trillion, if your requirement for 3 chars is strict, else you risk creating corrupt data, which would be very bad.
如果您对 3 个字符的要求严格,您将需要捕捉结果 >=1 万亿的情况,否则您可能会创建损坏的数据,这将是非常糟糕的。
回答by James L.
The modern, easy, built-in, highly customizable, and 'no-code' way: Intl.FormatNumber's formatfunction (compatibility graph)
现代、简单、内置、高度可定制和“无代码”的方式: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
Notes:
笔记:
- If you're using typescript add a
//@ts-ignore
beforenotation
(source) - And a list of all the keys in the
options
parameter: https://docs.w3cub.com/javascript/global_objects/numberformat/ - When using
style: 'currency'
, you must remove maximumFractionDigits, as it will figure this out for you.
- 如果您使用的是打字稿,请在
//@ts-ignore
之前添加一个notation
(源) - 以及
options
参数中所有键的列表:https: //docs.w3cub.com/javascript/global_objects/numberformat/ - 使用时
style: 'currency'
,您必须删除 maximumFractionDigits,因为它会为您解决这个问题。
回答by danilowoz
Code
代码
const SI_PREFIXES = [
{ value: 1, symbol: '' },
{ value: 1e3, symbol: 'k' },
{ value: 1e6, symbol: 'M' },
{ value: 1e9, symbol: 'G' },
{ value: 1e12, symbol: 'T' },
{ value: 1e15, symbol: 'P' },
{ value: 1e18, symbol: 'E' },
]
const abbreviateNumber = (number) => {
if (number === 0) return number
const tier = SI_PREFIXES.filter((n) => number >= n.value).pop()
const numberFixed = (number / tier.value).toFixed(1)
return `${numberFixed}${tier.symbol}`
}
abbreviateNumber(2000) // "2.0k"
abbreviateNumber(2500) // "2.5k"
abbreviateNumber(255555555) // "255.6M"
Test:
测试:
import abbreviateNumber from './abbreviate-number'
test('abbreviateNumber', () => {
expect(abbreviateNumber(0)).toBe('0')
expect(abbreviateNumber(100)).toBe('100')
expect(abbreviateNumber(999)).toBe('999')
expect(abbreviateNumber(1000)).toBe('1.0k')
expect(abbreviateNumber(100000)).toBe('100.0k')
expect(abbreviateNumber(1000000)).toBe('1.0M')
expect(abbreviateNumber(1e6)).toBe('1.0M')
expect(abbreviateNumber(1e10)).toBe('10.0G')
expect(abbreviateNumber(1e13)).toBe('10.0T')
expect(abbreviateNumber(1e16)).toBe('10.0P')
expect(abbreviateNumber(1e19)).toBe('10.0E')
expect(abbreviateNumber(1500)).toBe('1.5k')
expect(abbreviateNumber(1555)).toBe('1.6k')
expect(abbreviateNumber(undefined)).toBe('0')
expect(abbreviateNumber(null)).toBe(null)
expect(abbreviateNumber('100')).toBe('100')
expect(abbreviateNumber('1000')).toBe('1.0k')
})
回答by nimeresam
I'm using this function to get these values.
我正在使用此函数来获取这些值。
function Converter(number, fraction) {
let ranges = [
{ divider: 1, suffix: '' },
{ divider: 1e3, suffix: 'K' },
{ divider: 1e6, suffix: 'M' },
{ divider: 1e9, suffix: 'G' },
{ divider: 1e12, suffix: 'T' },
{ divider: 1e15, suffix: 'P' },
{ divider: 1e18, suffix: 'E' },
]
//find index based on number of zeros
let index = (Math.abs(number).toString().length / 3).toFixed(0)
return (number / ranges[index].divider).toFixed(fraction) + ranges[index].suffix
}
Each 3 digits has different suffix, that's what i'm trying to find firstly.
每个 3 位数都有不同的后缀,这就是我首先要找到的。
So, remove negative symbol if exists, then find how many 3 digits in this number.
因此,如果存在,则删除负号,然后找出该数字中有多少个 3 位数字。
after that find appropriate suffix based on previous calculation added to divided number.
之后根据先前的计算找到合适的后缀添加到被除数。
Converter(1500, 1)
Will return:
将返回:
1.5K
回答by Stefan Gabos
Here's another take on it. I wanted 123456 to be 123.4K instead of 0.1M
这是另一种看法。我希望 123456 是 123.4K 而不是 0.1M
function convert(value) {
var length = (value + '').length,
index = Math.ceil((length - 3) / 3),
suffix = ['K', 'M', 'B', 'T'];
if (length < 4) return value;
return (value / Math.pow(1000, index)).toFixed(1) + suffix[index - 1];
}