1

I have made an idle clicker game where you can click the dollar bill and get money and upgrade etc. I've just created an abbreviateNumber function where it shortens values over 1000 to 1k, 1m, 1t etc. I copied it off this previous thread: Convert long number into abbreviated string in JavaScript, with a special shortness requirement , but I'm not sure how to display with 2 decimal places rather than just 1, e.g. 1.05k, 2.65M etc.

I'd be grateful if someone could help me so I can carry on coding my game, my abbreviateNumber function is below:

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)  shortNum = shortValue.toFixed(1);
        newValue = shortValue+suffixes[suffixNum];
    }
    return newValue;
}
Community
  • 1
  • 1
David Lunt
  • 62
  • 4
  • 1
    _"I've just created an abbreviateNumber function"_ / _"I copied it off this previous thread"_ These two statements are at odds with one-another. Furthermore, you should not execute code that you do not understand. – Lightness Races in Orbit Apr 22 '17 at 12:15

2 Answers2

2

You can use logarithms and .toFixed() to implement this feature in just a few lines (the function you have there doesn't even determine the numbers' magnitude correctly):

var suffixes = ['', 'k', 'm', 'b', 't', 'qd', 'qt', 'sx', 'sp', 'o', 'n', 'd'];
var log1000 = Math.log(1000);

function abbrevNum(num, decimalPlaces) {
  if (num < 0) { return '-' + abbrevNum(-num, decimalPlaces); }
  if (num < 1000) { return num.toFixed(decimalPlaces); }

  var magnitude = Math.min(Math.floor(Math.log(num) / log1000), suffixes.length - 1);
  var adjusted = num / Math.pow(1000, magnitude);

  return adjusted.toFixed(decimalPlaces) + (suffixes[magnitude] || '');
}

console.log(abbrevNum(1323457, 2));
console.log(abbrevNum(13357, 2));
console.log(abbrevNum(0, 2));
console.log(abbrevNum(0.456, 2));
console.log(abbrevNum(-23456, 2));
console.log(abbrevNum(7.567e20, 2));
console.log(abbrevNum(9.23456e28, 2));
console.log(abbrevNum(8.235926e37, 2));
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • Hey, nice code. How would I use a variable with this function. I use it as follows: document.getElementById('balance').value = "$" + (abbrevNum(balance, 2)); but when it updates and shows balance, it says: $NaNundefined Do you know why this is??? – David Lunt Apr 22 '17 at 12:49
  • @DavidLunt You should be able to just pass the variable in like that if it holds a number. If it holds a string, you'd need to do `"$" + abbrevNum(Number(balance), 2)` – JLRishe Apr 22 '17 at 12:53
  • @JSRishe Thanks so much, it's now working :-) Turns out it was a string so your method of converting to number worked :-) – David Lunt Apr 22 '17 at 12:58
  • @DavidLunt -- I have to disagree about the "nice code". In particular: it does a complex floating-point calculation to determine the number `3`; it calls fixed-point on zero, and it doesn't work on numbers of a quadrillion or more. – Michael Lorton Apr 22 '17 at 14:33
  • @Malvolio It's not determining the number `3` anywhere. The complex calculation you're referring to is determining the number `6.907755278982137`. And this is done outside the function so it's only done once. Why is there a problem with calling `.toFixed()` on zero? And your answer doesn't work on numbers of a quadrillion or more either (so I'm puzzled as to why you bring up this criticism) but mine can easily be made to do so by extending the `suffixes` array (which I've now done), whereas your example would require more extensive changes to handle larger numbers. – JLRishe Apr 22 '17 at 14:57
  • The code uses natural logs for no obvious reason, and so calculates `3e` instead of `3`, but I don't think that redounds to its credit _at all_. My answer works perfectly well on numbers of a quadrillion or more without modification (using trillions), and if we were to agree on a new suffix (`p`?), it could easily be modified to handle. The answer here simply gives the wrong answer if it lacks an appropriate suffix, and I don't see how to fix it. – Michael Lorton Apr 22 '17 at 15:03
  • @JLRishe -- in fairness, I did not realize the code was intended for fix-points other than 2. Given that it is, calling `0.toFixed(n)` is sensible and I retract that criticism. – Michael Lorton Apr 22 '17 at 15:06
  • @Malvolio Fixed it to handle any size number. Just needed one more line. – JLRishe Apr 22 '17 at 15:08
  • @JLRishe -- I think it would be better handled with a `min` than an `if`. Also, you should probably short-circuit on `< 1000` instead of `< 1`. – Michael Lorton Apr 22 '17 at 15:28
  • @Malvolio Changed it to use min just before you made that comment. And I agree with short circuiting on 1000. – JLRishe Apr 22 '17 at 15:29
  • @JLRishe -- consider also using log10 instead of natural log. – Michael Lorton Apr 22 '17 at 15:34
  • @Malvolio The running time of `Math.log10` is 50x the running time of `Math.log` in Chrome, equal time in Firefox, and it's not even supported in IE. https://jsperf.com/ln-vs-log10 I think that's a compelling reason to use `Math.log`. It's just more "natural". – JLRishe Apr 22 '17 at 15:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/142354/discussion-between-malvolio-and-jlrishe). – Michael Lorton Apr 22 '17 at 17:56
-2

You are looking for the standard Javascript method .toFixed:

var n = 1.4
n.toFixed(2)

yields "1.40". And, although you didn't ask for review, that code is sloppy and illegible. How about:

const formatter = (suffix, divisor) => n => (n / divisor).toFixed(2) + suffix;
const forms = [
  n => n.toFixed(2),
  formatter("k", 1000),
  formatter("m", 1000000),
  formatter("b", 1000000000),
  formatter("t", 1000000000000),
]; 
const zeros = n => n && Math.floor(Math.log10(n) / 3);
const format = n => (n < 0) ? ("-" + format(-n)): forms[Math.min(zeros(n), forms.length-1)](n);

console.log(format(0));
console.log(format(101));
console.log(format(1010));
console.log(format(-1010));
console.log(format(1010000));
console.log(format(1010000000));
console.log(format(1010000000000));
console.log(format(1010000000000000));

Does the same thing in three lines of code.

Michael Lorton
  • 43,060
  • 26
  • 103
  • 144