JavaScript Tips | 文字列ユーティリティ:業務用 - 金額フォーマット

JavaScript JavaScript
スポンサーリンク

何を作るのか:「業務で使える金額フォーマット」

ここで目指すのは、「数値を“人間が業務で読みやすい金額表示”に変換するユーティリティ」です。

例えば、こんな変換を安定して行いたいイメージです。

formatCurrency(0);        // "0"
formatCurrency(1000);     // "1,000"
formatCurrency(1234567);  // "1,234,567"
formatCurrency(-5000);    // "-5,000"
JavaScript

画面表示、PDF、CSV、メール文面など、
「金額を見せる場面」は業務システムに山ほどあります。

だからこそ、「毎回バラバラに書かず、1 箇所に“正しいルール”を閉じ込める」のが大事になります。


金額フォーマットで最低限決めておくべきルール

3 桁ごとにカンマを入れる

まずは一番基本のルールです。

1000      → 1,000
1234567   → 1,234,567
-9876543  → -9,876,543

人間が桁を読み間違えないようにするために、
「整数部分を 3 桁ごとに区切る」のが金額表示の定番です。

小数をどう扱うかを決める

金額によっては、小数が必要なケースもあります。

1234.5    → 1,234.5
1234.56   → 1,234.56

業務によっては、

  • 常に小数 2 桁に揃える(1,234.50
  • 小数は表示しない(四捨五入して整数にする)

などのルールが変わるので、
「小数をどう扱うか」をユーティリティの引数で切り替えられるようにすると便利です。

マイナス金額の扱い

赤字や返金などで、マイナス金額も普通に出てきます。

-1000 → -1,000

ここはシンプルに、「符号はそのまま、数値部分だけフォーマット」という方針にしておくと分かりやすいです。


まずは「整数金額」をフォーマットする関数

formatCurrencyInt の実装

小数なしの「整数金額」を対象にした、シンプルな版から作ります。

function formatCurrencyInt(value) {
  if (value == null || value === "") {
    return "";
  }

  const num = Number(value);

  if (!Number.isFinite(num)) {
    return "";
  }

  const sign = num < 0 ? "-" : "";

  const absStr = Math.abs(num).toString();

  const withComma = absStr.replace(/\B(?=(\d{3})+(?!\d))/g, ",");

  return sign + withComma;
}
JavaScript

重要なポイントをかみ砕いて説明する

ここが金額フォーマットの基礎なので、丁寧に見ていきます。

1. null / 空文字は「表示しない」と決める

if (value == null || value === "") {
  return "";
}
JavaScript

フォーム入力や API のレスポンスでは、
null"" が普通に混ざります。

ここでは、「値がないものは表示しない(空文字)」という方針にしています。
業務によっては "0" を返したい場合もあるので、そこはプロジェクトのルールで決めてください。

2. 数値に変換して、有限かどうかをチェック

const num = Number(value);

if (!Number.isFinite(num)) {
  return "";
}
JavaScript

"1000" のような文字列も受け入れたいので、
一度 Number(...) で数値に変換しています。

NaNInfinity のような「変な値」は、
金額として扱えないので空文字を返す、という割り切りです。

3. 符号と絶対値を分けて扱う

const sign = num < 0 ? "-" : "";

const absStr = Math.abs(num).toString();
JavaScript

マイナス金額を扱うために、

  • 符号("-" または ""
  • 絶対値の文字列("1000" など)

に分けておきます。

カンマを入れるのは「絶対値の部分」だけにして、
最後に符号をくっつける、という流れです。

4. 正規表現で 3 桁ごとにカンマを入れる

const withComma = absStr.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
JavaScript

この正規表現は一見難しく見えますが、
「3 桁ごとにカンマを入れる定番パターン」です。

ざっくり言うと、

  • \d{3} … 数字 3 桁
  • (?=(\d{3})+(?!\d)) … 「後ろに 3 桁の塊が続く位置」を探す
  • \B … 単語境界ではない位置(先頭にはマッチしないようにする)

という意味で、
「先頭以外で、後ろに 3 桁の塊が続く位置にカンマを差し込む」動きをします。

結果として、

"1"        → "1"
"12"       → "12"
"123"      → "123"
"1234"     → "1,234"
"12345"    → "12,345"
"1234567"  → "1,234,567"

のように、きれいに 3 桁区切りになります。


小数を含む金額をフォーマットする

小数桁数を指定できる formatCurrency

次に、「小数をどう扱うか」を指定できる版を作ります。

function formatCurrency(value, options = {}) {
  const { fractionDigits = null } = options;

  if (value == null || value === "") {
    return "";
  }

  const num = Number(value);

  if (!Number.isFinite(num)) {
    return "";
  }

  const sign = num < 0 ? "-" : "";

  const absNum = Math.abs(num);

  let intPartStr;
  let fracPartStr = "";

  if (fractionDigits == null) {
    const [intStr, fracStr = ""] = absNum.toString().split(".");
    intPartStr = intStr;
    fracPartStr = fracStr;
  } else {
    const fixed = absNum.toFixed(fractionDigits);
    const [intStr, fracStr = ""] = fixed.split(".");
    intPartStr = intStr;
    fracPartStr = fracStr;
  }

  const intWithComma = intPartStr.replace(/\B(?=(\d{3})+(?!\d))/g, ",");

  const result =
    fracPartStr && fractionDigits !== 0
      ? intWithComma + "." + fracPartStr
      : intWithComma;

  return sign + result;
}
JavaScript

重要なポイントをかみ砕いて説明する

ここでのキモは「小数をどう扱うか」です。

1. fractionDigits が null かどうかで分岐する

const { fractionDigits = null } = options;
JavaScript

fractionDigits は「小数点以下何桁にするか」を表します。

  • null → 元の数値の小数をそのまま使う
  • 0 以上の整数 → toFixed(fractionDigits) で丸める

というルールにしています。

2. 小数をそのまま使うパターン

if (fractionDigits == null) {
  const [intStr, fracStr = ""] = absNum.toString().split(".");
  intPartStr = intStr;
  fracPartStr = fracStr;
}
JavaScript

1234.5"1234.5""1234""5" に分割。
1234"1234""1234""" に分割。

この場合、小数は「元の値のまま」表示されます。

formatCurrency(1234.5);        // "1,234.5"
formatCurrency(1234.567);      // "1,234.567"
formatCurrency(1234);          // "1,234"
JavaScript

3. 小数桁数を固定するパターン

} else {
  const fixed = absNum.toFixed(fractionDigits);
  const [intStr, fracStr = ""] = fixed.split(".");
  intPartStr = intStr;
  fracPartStr = fracStr;
}
JavaScript

toFixed(fractionDigits) は、

  • 小数を指定桁数に丸める
  • 必要なら 0 で埋める

という動きをします。

(1234.5).toFixed(2);    // "1234.50"
(1234).toFixed(2);      // "1234.00"
(1234.567).toFixed(2);  // "1234.57"
JavaScript

これを使うことで、

formatCurrency(1234.5, { fractionDigits: 2 });   // "1,234.50"
formatCurrency(1234,   { fractionDigits: 2 });   // "1,234.00"
formatCurrency(1234.567, { fractionDigits: 2 }); // "1,234.57"
JavaScript

のように、「常に小数 2 桁で揃える」表示ができます。

4. 整数部分だけにカンマを入れる

const intWithComma = intPartStr.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
JavaScript

ここはさっきと同じで、
「整数部分だけ 3 桁区切りにする」処理です。

小数部分にはカンマを入れません。

5. 小数部分があれば . を付けて結合する

const result =
  fracPartStr && fractionDigits !== 0
    ? intWithComma + "." + fracPartStr
    : intWithComma;
JavaScript

小数部分が空でなければ、"." + fracPartStr を付けます。

fractionDigits === 0 のときは、小数を表示しないので整数だけになります。


実際の動きを例で確認する

整数だけの例

formatCurrency(0);          // "0"
formatCurrency(1000);       // "1,000"
formatCurrency(1234567);    // "1,234,567"
formatCurrency(-5000);      // "-5,000"
JavaScript

小数をそのまま表示する例

formatCurrency(1234.5);     // "1,234.5"
formatCurrency(1234.567);   // "1,234.567"
formatCurrency(-9876.5);    // "-9,876.5"
JavaScript

小数桁数を固定する例

formatCurrency(1234.5, { fractionDigits: 2 });
// "1,234.50"

formatCurrency(1234, { fractionDigits: 2 });
// "1,234.00"

formatCurrency(1234.567, { fractionDigits: 2 });
// "1,234.57"

formatCurrency(1234.567, { fractionDigits: 0 });
// "1,235"
JavaScript

実務での使いどころと設計のポイント

「表示用」と「計算用」を分ける

ここで作っているのは、あくまで「表示用の文字列」を作る関数です。

計算は必ず数値で行い、
画面や帳票に出す直前で formatCurrency を通して文字列にする、
という流れにしておくと、バグが減ります。

const total = items.reduce((sum, item) => sum + item.price, 0);

const displayTotal = formatCurrency(total, { fractionDigits: 0 });
JavaScript

計算に formatCurrency の結果(文字列)を使わない、というのが大事なルールです。

「フォーマットのルール」を 1 箇所に閉じ込める

金額表示は、画面ごとにバラバラに書き始めると、すぐに崩れます。

ある画面ではカンマあり、
別の画面ではカンマなし、
どこかでは小数 2 桁、別のところでは整数だけ、

という状態になると、ユーザーも混乱します。

だからこそ、

export function formatCurrency(...) { ... }
export function formatCurrencyInt(...) { ... }
JavaScript

のようなユーティリティを 1 箇所に置いて、

  • 金額を表示するときは必ずここを通す
  • 小数桁数のルールもここで一元管理する

という設計にしておくと、
「業務システムとしての見た目の一貫性」が保ちやすくなります。


ちょっとだけ手を動かしてみる

コンソールで、次のあたりを試してみてください。

formatCurrencyInt(0);
formatCurrencyInt(1000);
formatCurrencyInt(-1234567);

formatCurrency(1234.5);
formatCurrency(1234.567);
formatCurrency(1234.5, { fractionDigits: 2 });
formatCurrency(1234.567, { fractionDigits: 2 });
formatCurrency(1234.567, { fractionDigits: 0 });
JavaScript

「どの値がどうフォーマットされるか」
「カンマと小数がどう付くか」

を、自分の目で確認してみてください。

そのうえで、自分のプロジェクトに

export function formatCurrencyInt(value) { ... }
export function formatCurrency(value, options) { ... }
JavaScript

を置いて、

金額を表示したくなったら、
value.toLocaleString() ではなく、
必ず自分たちのルールを持った formatCurrency を通す、という習慣をつけてみてください。

その瞬間、あなたの金額表示は、
場当たり的なフォーマットから、
「意図と一貫性を備えた“業務レベルの金額フォーマットユーティリティ”」に変わっていきます。

タイトルとURLをコピーしました