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 an
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:
//@ts-ignore
before notation
(source)options
parameter: https://docs.w3cub.com/javascript/global_objects/numberformat/style: 'currency'
, you must remove maximumFractionDigits, as it will figure this out for you.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;
}
Here's another take on it. I wanted 123456 to be 123.4K instead of 0.1M
function convert(value) {
var length = (value + '').length,
index = Math.ceil((length - 3) / 3),
suffix = ['K', 'M', 'G', 'T'];
if (length < 4) return value;
return (value / Math.pow(1000, index))
.toFixed(1)
.replace(/\.0$/, '') + suffix[index - 1];
}
var tests = [1234, 7890, 123456, 567890, 800001, 2000000, 20000000, 201234567, 801234567, 1201234567];
for (var i in tests)
document.writeln('<p>convert(' + tests[i] + ') = ' + convert(tests[i]) + '</p>');
@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
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.
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];
}