javascript - Formatera ett nummer som 2,5K om det är tusen eller mer, annars 900


Jag behöver visa ett valutavärde i formatet 1K som motsvarar ett tusen, eller 1.1K, 1.2K, 1.9K osv., om det inte är ett jämnt tusental, annars om det är under tusen, visa normalt 500, 100, 250 osv., med JavaScript för att formatera numret?

Svar #1

En mer generaliserad version:

function nFormatter(num, digits) {
  const lookup = [
    { 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 regexp = /\.0+$|(?<=\.[0-9]*[1-9])0+$/;
  const item = lookup.findLast(item => num >= item.value);
  return item ? (num / item.value).toFixed(digits).replace(regexp, "").concat(item.symbol) : "0";
}

/*
 * Tests
 */
const tests = [
  { num: 0, digits: 1 },
  { num: 12, digits: 1 },
  { num: 1234, digits: 1 },
  { num: 100000000, digits: 1 },
  { num: 299792458, digits: 1 },
  { num: 759878, digits: 1 },
  { num: 759878, digits: 0 },
  { num: 123, digits: 1 },
  { num: 123.456, digits: 1 },
  { num: 123.456, digits: 2 },
  { num: 123.456, digits: 4 }
];
tests.forEach(test => {
  console.log("nFormatter(%f, %i) = %s", test.num, test.digits, nFormatter(test.num, test.digits));
});

lookup innehåller SI-prefix sorterade i stigande ordning. regexp matchar efterföljande nollor (och punkten, om möjligt) i talet.

Notera:

Array.findLast kräver ES2023. Om du får ett fel, ändra lookup.findLast(...) till lookup.slice().reverse().find(...)

Svar #2

ES2020 lägger till stöd för detta i Intl.NumberFormat genom att använda notation på följande sätt:

let formatter = Intl.NumberFormat('en', { notation: 'compact' });
// example 1
let million = formatter.format(1e6);
// example 2
let billion = formatter.format(1e9);
// print
console.log(million == '1M', billion == '1B');

Notera som visas ovan, att det andra exemplet producerar 1B istället för 1G. NumberFormat-specifikationer:

  • https://developer.mozilla.org/sv-SE/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
  • https://tc39.es/ecma402#numberformat-objects

Observera att inte alla webbläsare för tillfället stöder ES2020, så du kan behöva denna Polyfill: https://formatjs.io/docs/polyfills/intl-numberformat

Svar #3

Det låter som att detta borde fungera för dig:

function kFormatter(num) {
    return Math.abs(num) > 999 ? Math.sign(num)*((Math.abs(num)/1000).toFixed(1)) + 'k' : Math.sign(num)*Math.abs(num)
}
    
console.log(kFormatter(1200)); // 1.2k
console.log(kFormatter(-1200)); // -1.2k
console.log(kFormatter(900)); // 900
console.log(kFormatter(-900)); // -900

Svar #4

Här är en enkel lösning som undviker alla if-satser (med kraften av Math).

var SI_SYMBOL = ["", "k", "M", "G", "T", "P", "E"];

function abbreviateNumber(number){

    // what tier? (determines SI symbol)
    var tier = Math.log10(Math.abs(number)) / 3 | 0;

    // if zero, we don't need a suffix
    if(tier == 0) return number;

    // get suffix and determine scale
    var suffix = SI_SYMBOL[tier];
    var scale = Math.pow(10, tier * 3);

    // scale the number
    var scaled = number / scale;

    // format number and add suffix
    return scaled.toFixed(1) + suffix;
}

Bonus Meme

Vad står SI för?

Svar #5

Ytterligare förbättra Salmans svar eftersom det returnerar nFormatter(33000) som 33.0K

function nFormatter(num) {
     if (num >= 1000000000) {
        return (num / 1000000000).toFixed(1).replace(/\.0$/, '') + 'G';
     }
     if (num >= 1000000) {
        return (num / 1000000).toFixed(1).replace(/\.0$/, '') + 'M';
     }
     if (num >= 1000) {
        return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'K';
     }
     return num;
}

nu nFormatter(33000)=33K

Svar #6

Ett enkelt tillvägagångssätt har den bästa läsbarheten och använder minst minne. Ingen anledning att överarbeta med användning av regex, map-objekt, Math-objekt, for-loopar osv.

Formatera kontantvärde med K

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

console.log(formatCash(2500));

Formaterar kontantvärde med 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));

Att använda negativa tal

let format;
const number = -1235000;

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

Svar #7

/**
 * Shorten number to thousands, millions, billions, etc.
 * http://en.wikipedia.org/wiki/Metric_prefix
 *
 * @param {number} num Number to shorten.
 * @param {number} [digits=0] The number of digits to appear after the decimal point.
 * @returns {string|number}
 *
 * @example
 * // returns '12.5k'
 * shortenLargeNumber(12543, 1)
 *
 * @example
 * // returns '-13k'
 * shortenLargeNumber(-12567)
 *
 * @example
 * // returns '51M'
 * shortenLargeNumber(51000000)
 *
 * @example
 * // returns 651
 * shortenLargeNumber(651)
 *
 * @example
 * // returns 0.12345
 * shortenLargeNumber(0.12345)
 */
function shortenLargeNumber(num, digits) {
    var units = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'],
        decimal;

    for(var i=units.length-1; i>=0; i--) {
        decimal = Math.pow(1000, i+1);

        if(num <= -decimal || num >= decimal) {
            return +(num / decimal).toFixed(digits) + units[i];
        }
    }

    return num;
}

Tack @Cos för kommentaren, jag tog bort beroendet av Math.round10.

Svar #8

Ge erkännande till Waylon Flinn om du gillar detta

Detta förbättrades från hans mer eleganta metod för att hantera negativa tal och ".0"-fallet.

Ju färre loopar och "if"-fall du har, desto bättre enligt min åsikt.

function abbreviateNumber(number) {
    const SI_POSTFIXES = ["", "k", "M", "G", "T", "P", "E"];
    const sign = number < 0 ? '-1' : '';
    const absNumber = Math.abs(number);
    const tier = Math.log10(absNumber) / 3 | 0;
    // if zero, we don't need a prefix
    if(tier == 0) return `${absNumber}`;
    // get postfix and determine scale
    const postfix = SI_POSTFIXES[tier];
    const scale = Math.pow(10, tier * 3);
    // scale the number
    const scaled = absNumber / scale;
    const floored = Math.floor(scaled * 10) / 10;
    // format number and add postfix as suffix
    let str = floored.toFixed(1);
    // remove '.0' case
    str = (/\.0$/.test(str)) ? str.substr(0, str.length - 2) : str;
    return `${sign}${str}${postfix}`;
}

jsFiddle med testfall -> https://jsfiddle.net/qhbrz04o/9/

Svar #9

detta är ganska elegant.

function formatToUnits(number, precision) {
  const abbrev = ['', 'k', 'm', 'b', 't'];
  const unrangifiedOrder = Math.floor(Math.log10(Math.abs(number)) / 3)
  const order = Math.max(0, Math.min(unrangifiedOrder, abbrev.length -1 ))
  const suffix = abbrev[order];

  return (number / Math.pow(10, order * 3)).toFixed(precision) + suffix;
}

formatToUnits(12345, 2)
==> "12.35k"
formatToUnits(0, 3)
==> "0.000"

Svar #10

Jag tror att det kan vara en lösning.

var unitlist = ["","K","M","G"];
function formatnumber(number){
   let sign = Math.sign(number);
   let unit = 0;
   
   while(Math.abs(number) >= 1000)
   {
     unit = unit + 1; 
     number = Math.floor(Math.abs(number) / 100)/10;
   }
   console.log(sign*Math.abs(number) + unitlist[unit]);
}
formatnumber(999);
formatnumber(1234);
formatnumber(12345);
formatnumber(123456);
formatnumber(1234567);
formatnumber(12345678);
formatnumber(1000);
formatnumber(-999);
formatnumber(-1234);
formatnumber(-12345);
formatnumber(-123456);
formatnumber(-1234567);
formatnumber(-12345678);

Svar #11

Det enklaste och lättaste sättet att göra detta är

new Intl.NumberFormat('en-IN', { 
    notation: "compact",
    compactDisplay: "short",
    style: 'currency',
    currency: 'INR'
}).format(1000).replace("T", "K")

Detta fungerar för vilket nummer som helst. Inklusive L Cr osv.

OBS: Fungerar inte i Safari.

Svar #12

Kort och generell metod

Du kan göra COUNT_FORMATS-konfigurationsobjektet så långt eller kort som du vill, beroende på omfattningen av värdena du testat.

// Configuration    
const COUNT_FORMATS =
[
  { // 0 - 999
    letter: '',
    limit: 1e3
  },
  { // 1,000 - 999,999
    letter: 'K',
    limit: 1e6
  },
  { // 1,000,000 - 999,999,999
    letter: 'M',
    limit: 1e9
  },
  { // 1,000,000,000 - 999,999,999,999
    letter: 'B',
    limit: 1e12
  },
  { // 1,000,000,000,000 - 999,999,999,999,999
    letter: 'T',
    limit: 1e15
  }
];
    
// Format Method:
function formatCount(value)
{
  const format = COUNT_FORMATS.find(format => (value < format.limit));

  value = (1000 * value / format.limit);
  value = Math.round(value * 10) / 10; // keep one decimal number, only if needed

  return (value + format.letter);
}

// Test:
const test = [274, 1683, 56512, 523491, 9523489, 5729532709, 9421032489032];
test.forEach(value => console.log(`${ value } >>> ${ formatCount(value) }`));

Svar #13

Du kan använda d3-format-paketet som är modellerat efter Python Advanced String Formatting PEP3101 :

var f = require('d3-format')
console.log(f.format('.2s')(2500)) // displays "2.5k"

Relaterade artiklar