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

在 JavaScript 中,如何编写一个函数,将给定的[编辑: 正整数正整数]数字(低于1000亿)转换为一个3个字母的缩写——其中0-9和 A-Z/A-Z 被计为一个字母,但点(因为它在许多比例字体中是如此微小)不会,并将被忽略的字母限制?

这个问题和 这个有用的线索有关,但是不一样; 例如,函数会转到哪里,比如“123456-> 1.23 k”(“123.5 k”是5个字母) ,我在寻找一个函数,它的值是“123456-> 0.1 m”(“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;
}
77075 次浏览

Based on my answer at https://stackoverflow.com/a/10600491/711085 , your answer is actually slightly shorter to implement, by using .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.)

> 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.

I believe ninjagecko's solution doesn't quite conform with the standard you wanted. The following function does:

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.

Edit by Philipp follows: With the following changes it fits with all requirements perfectly!

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;
}

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

After some playing around, this approach seems to meet the required criteria. Takes some inspiration from @chuckator's answer.

function abbreviateNumber(value) {


if (value <= 1000) {
return value.toString();
}


const numDigits = (""+value).length;
const suffixIndex = Math.floor(numDigits / 3);


const normalisedValue = value / Math.pow(1000, suffixIndex);


let precision = 2;
if (normalisedValue < 1) {
precision = 1;
}


const suffixes = ["", "k", "m", "b","t"];
return normalisedValue.toPrecision(precision) + suffixes[suffixIndex];
}

I think you cant try this numeraljs/

If you want convert 1000 to 1k

console.log(numeral(1000).format('0a'));

and if you want convert 123400 to 123.4k try this

console.log(numeral(123400).format('0.0a'));

Intl is the Javascript standard 'package' for implemented internationalized behaviours. Intl.NumberFormatter is specifically the localized number formatter. So this code actually respects your locally configured thousands and decimal separators.

intlFormat(num) {
return new Intl.NumberFormat().format(Math.round(num*10)/10);
}


abbreviateNumber(value) {
let num = Math.floor(value);
if(num >= 1000000000)
return this.intlFormat(num/1000000000)+'B';
if(num >= 1000000)
return this.intlFormat(num/1000000)+'M';
if(num >= 1000)
return this.intlFormat(num/1000)+'k';
return this.intlFormat(num);
}

abbreviateNumber(999999999999) // Gives 999B

Related question: Abbreviate a localized number in JavaScript for thousands (1k) and millions (1m)

            function converse_number (labelValue) {


// Nine Zeroes for Billions
return Math.abs(Number(labelValue)) >= 1.0e+9


? Math.abs(Number(labelValue)) / 1.0e+9 + "B"
// Six Zeroes for Millions
: Math.abs(Number(labelValue)) >= 1.0e+6


? Math.abs(Number(labelValue)) / 1.0e+6 + "M"
// Three Zeroes for Thousands
: Math.abs(Number(labelValue)) >= 1.0e+3


? Math.abs(Number(labelValue)) / 1.0e+3 + "K"


: Math.abs(Number(labelValue));


}

alert(converse_number(100000000000));

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'

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')
})

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.

So, remove negative symbol if exists, then find how many 3 digits in this number.

after that find appropriate suffix based on previous calculation added to divided number.

Converter(1500, 1)

Will return:

1.5K

@nimesaram

Your solution will not be desirable for the following case:

Input 50000
Output 50.0k

Following solution will work fine.

const convertNumberToShortString = (
number: number,
fraction: number
) => {
let newValue: string = number.toString();
if (number >= 1000) {
const ranges = [
{ divider: 1, suffix: '' },
{ divider: 1e3, suffix: 'k' },
{ divider: 1e6, suffix: 'm' },
{ divider: 1e9, suffix: 'b' },
{ divider: 1e12, suffix: 't' },
{ divider: 1e15, suffix: 'p' },
{ divider: 1e18, suffix: 'e' }
];
//find index based on number of zeros
const index = Math.floor(Math.abs(number).toString().length / 3);
let numString = (number / ranges[index].divider).toFixed(fraction);
numString =
parseInt(numString.substring(numString.indexOf('.') + 1)) === 0
? Math.floor(number / ranges[index].divider).toString()
: numString;
newValue = numString + ranges[index].suffix;
}
return newValue;
};


// Input 50000
// Output 50k
// Input 4500
// Output 4.5k

Approach 1: Built-in library

I would recommend using Javascript's built-in library method Intl.NumberFormat

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

Approach 2: No library

But you can also create these abbreviations with simple if statements, and without the complexity of Math, maps, regex, for-loops, etc.

Formatting Cash value with K

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


console.log(formatCash(2500));

Formatting Cash value with K M B T

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));

Using negative numbers

let format;
const number = -1235000;


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

Here's another take on it. I wanted 123456 to be 123.4K instead of 0.1M

UPDATE 2022, Dec. 5

Added support for negative values and non-integer values

function convert(value) {


var length = (Math.abs(parseInt(value, 10)) + '').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)
.replace(/\.0$/, '') + suffix[index - 1];


}


var tests = [1234, 7890, -990123467, 123456, 750000.1234, 567890, 800001, 2000000, 20000000, 201234567, 801234567, 1201234567];
for (var i in tests)
document.writeln('<p>convert(' + tests[i] + ') = ' + convert(tests[i]) + '</p>');

The modern, easy, built-in, highly customizable, and 'no-code' way: Intl.FormatNumber 's format function (compatibility graph)

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:

Use as a Number Prototype

For easy and direct you. Simply make a prototype of it. Here is an example.

Number.prototype.abbr = function (decimal = 2): string {
const notations = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'],
i = Math.floor(Math.log(this) / Math.log(1000));
return `${parseFloat((this / Math.pow(1000, i)).toFixed(decimal))}${notations[i]}`;
};

Indian Currency format to (K, L, C) Thousand, Lakh, Crore

const formatCash = n => {
if (n < 1e3) return n;
if (n >= 1e3 && n < 1e5) return +(n / 1e3).toFixed(1) + "K";
if (n >= 1e5 && n <= 1e6) return +(n / 1e5).toFixed(1) + "L";
if (n >= 1e6 && n <= 1e9) return +(n / 1e7).toFixed(1) + "C";
};

Even more Abbreviation?

Let's make "Qa" as Quadrillions and "Qi" as Quintillions... maybe "Sx" Sextillions, and So on...

if (num >= 1e3 !& num >= 1e6) {num2 = num/1e3 + "K";};
if (num >= 1e6 !& num >= 1e9) {num2 = num/1e6 + "M";};
if (num >= 1e9 !& num >= 1e12) {num2 = num/1e9 + "B";};
if (num >= 1e12 !& num >= 1e15) {num2 = num/1e12 + "T";};
if (num >= 1e15 !& num >= 1e18) {num2 = num/1e15 + "Qa";};
if (num >= 1e18 !& num >= 1e21) {num2 = num/1e18 + "Qi";};
if (num >= 1e21 !& num >= 1e24) {num2 = num/1e21 + "Sx";};
if (num >= 1e24 !& num >= 1e27) {num2 = num/1e24 + "Sp";};
if (num >= 1e27 !& num >= 1e30) {num2 = num/1e27 + "Oc";};
if (num >= 1e30 !& num >= 1e93) {num2 = num/1e30 + "No";};
if (num >= 1e33 !& num >= 1e36) {num2 = num/1e33 + "Dc";};
if (num >= 1e36 !& num >= 1e39) {num2 = num/1e36 + "UDc";};
if (num >= 1e39) {num2 = num/1e39 + "DDc";};

Pure Javascript


n --> 292234567890 to 292,234,567,890
s --> 292,234,567,890 to 292,234,567,890 + K | M | B | T | q | Q | s | 292,234,567,8900 | 292,234,567,8901 | 292,234,567,8902 | 292,234,567,8903 | 292,234,567,8904
s --> 292,234,567,890B to 292.2B
return --> Remove 4th digit 292.2B to 292B


function format(n) {n=n.toLocaleString();
let s = n+('KMBTqQsSondU'.split('')[n.split(',').length-2]||'')
s = s.replace(/,([1-9])[\d,.]+|,[\d,.]+/g,(m,a)=>a?'.'+a:'')
return s.replace(/(\d{3})\.\d/, '$1');
}




//Just for show
let negative = [-1991078910902345678907890, -991708910902345678907890,-10902345678907890, -1000234567890, -1234567890, -12345678, -1234567, -12345, -1234, -123, -12, -1, 0]
let positive = [1991078910902345678907890, 991078910902345678907890,10902345678907890, 1000234567890, 1234567890, 12345678, 1234567, 12345, 1234, 123, 12, 1, 0]
let table = '<table cellspacing="0"><tbody>';
negative.forEach(function(x){ table += '<tr><td style="padding-right: 30px;">'+x+'</td><td>'+ format(x)+'</td></tr>'})
table +='</tbody></table><table cellspacing="0"><tbody>'
positive.forEach(function(x){ table += '<tr><td style="padding-right: 30px;">'+x+'</td><td>'+ format(x)+'</td></tr>'})
document.body.innerHTML = table+'</tbody></table>';
tr:nth-child(odd) {background: #f8f8f8;}
td {padding: 5px 10px;}
table {
font-size: 13px;
display: inline-table;
padding-right: 30px;
}

Pure Javascript

Pros: Native Built in Solution
Cons: Maxes out at Trillion

function format(x) {let o={maximumFractionDigits: 1,notation: "compact",compactDisplay: "short"};
let a=x<0,n=x*Math.sign(x);s=new Intl.NumberFormat('en-US', o).format(n);return a?'-'+s:s;
}






//Just for show
let negative = [-1991078910902345678907890, -991078910902345678907890,-10902345678907890, -1000234567890, -1234567890, -12345678, -1234567, -12345, -1234, -123, -12, -1, 0]
let positive = [1991078910902345678907890, 991078910902345678907890,10902345678907890, 1000234567890, 1234567890, 12345678, 1234567, 12345, 1234, 123, 12, 1, 0]
let table = '<table cellspacing="0"><tbody>';
negative.forEach(function(x){ table += '<tr><td style="padding-right: 10px;">'+x+'</td><td>'+ format(x)+'</td></tr>'})
table +='</tbody></table><table cellspacing="0"><tbody>'
positive.forEach(function(x){ table += '<tr><td style="padding-right: 10px;">'+x+'</td><td>'+ format(x)+'</td></tr>'})
document.body.innerHTML = table+'</tbody></table>';
tr:nth-child(odd) {background: #f8f8f8;}
td {padding: 5px 10px;}
table {
font-size: 12px;
display: inline-table;
padding-right: 10px;
}