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