【JavaScript】PHP の number_format 関数を少数や記号の制御も含めて再現する

 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);
}
>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG