number_format は PHP の関数の一つでその名の通り数値のフォーマットを定めます。主に千の位ごとにカンマをつける目的で使われます。number_format は単にカンマをつけるのみでなく、表示する少数の範囲、少数や千の位の区切りに使う記号も指定できます。この記事ではこれを JavaScript で再実装するコードを紹介します。
PHP: number_format – Manual
ちなみにこれを作る動機としては、デザインで数値の表現方式を number_format 関数で指定されていた場合、JavaScript 独自にカンマをつけたりなんだりするよりも number_format 関数相当を作ってそれを通した方がが安心、というその程度のものです。
ソースコードとデモが次です。
クリックでソースコードを展開
/** * PHP の number_format を JavaScript で実装 * @see ドキュメント https://www.php.net/manual/ja/function.number-format.php * @see 本家 https://github.com/php/php-src/blob/07fa13088e1349f4b5a044faeee57f2b34f6b6e4/ext/standard/math.c#L1011 * @param {Number|String} num 数値を表現する文字列でもOK * @param {Number|String} decimals 数値を表現する文字列でもOK * @param {String} decimal_separator * @param {String} thousands_separator */ function number_format(num, decimals = 0, decimal_separator = '.', thousands_separator = ',') { let i; num = +num; decimals = +decimals < 0 ? 0 : decimals; // 少数 // 文字列で数値を構築(誤差対策も兼ねます) let strnum = round(num, decimals); let addZero = ""; if (!strnum.toString().includes('.')) { if (decimals > 0) { addZero += "."; } for (i = 0; i < decimals; i++) { addZero += "0"; } } else { let decimal = strnum.toString().split('.')[1]; for (i = 0; i < decimals - decimal.length; i++) { addZero += "0"; } } strnum = `${strnum}${addZero}`.replace('.', decimal_separator);// 小数点を置き換え // 千の位区切り let sign = '';// 後の文字列操作で符号が邪魔なので避難 if(num < 0) { strnum = strnum.slice(1); sign = '-'; } let integral = strnum.split(decimal_separator)[0].split('');// 整数部を配列形式で抜き出し integral = splitArrayByEqualSize(integral.reverse(), 3)// 1の位から数えるためにchar[]を反転 .map(t => t.join(''))// 3桁ずつまとめる .join(thousands_separator) .split('') .reverse()// 文字列に復元 .join('') return [sign, integral, decimals === 0 ? '' : decimal_separator, strnum.split(decimal_separator)[1]].join(''); } /** * 配列を要素 n 個の複数の小さい配列に分割。余りは最後の配列に入れる(最後の配列だけ n より小さいことがある)。 * @param {any[]} arr * @param {Number} n * @return {any[][]} */ function splitArrayByEqualSize(arr, n){ return arr.reduce( (pre, c, i) => i % n ? pre : [...pre, arr.slice(i, i + n ) ], []); } /** * 任意桁数での四捨五入 * @param {Number} numberArg * @param {Number} precisionArg * @return {Number} */ function round(numberArg, precisionArg) { const shift = (number, precision, reverseShift) => { if (reverseShift) { precision = -precision; } const numArray = ('' + number).split('e'); return +(numArray[0] + 'e' + (numArray[1] ? +numArray[1] + precision : precision)); }; return shift(Math.round(shift(numberArg, precisionArg, false)), precisionArg, true); }