この記事を書く数時間前、数値をカンマ区切りにしたいと思って手っ取り早くQiitaで手法を調べて使用したところ、「小数点以下を3桁までしか表示しない(toLocaleString
)」や「小数点以下の数列もカンマで区切ってしまう(正規表現)」など、小数点以下について考慮していないものばかりでちょっと面喰いました。
var n = 123456789.01234;
function separate(num){
return String(num).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
}
separate(n);
// "123,456,789.01,234"
n.toLocaleString();
// "123,456,789.012"
カンマ区切りをするようなものは大きな数値であることが殆どなので、小数点以下を考慮する必要があまりないのでしょうね。ですがいずれにせよ、私の場合は考慮したかったので、以下のように正規表現を書き換えてみました。
const comma = num => isNaN(num) || String(Number(num)).replace(/(?<!\.\d*?)(\d)(?=(\d{3})+(?!\d))/g, '$1,');
comma(n);
// "123,456,789.01234"
否定後読みによってピリオドが前方に無いことを確認させています。また数値に見えて数値でない文字列(123.456.789
など)をはじくため、isNaN
で数値かどうかのチェックもしています。
「これで完璧!」と思いきや、一つ問題が。
SafariとIEでは否定後読みが(というよりは後読み系が)できません。
IEは別にどうでもいいとして、Safari未サポートは問題ですね。
ですので、否定後読みを無理矢理できるようにする裏ワザ(Javascriptでの正規表現の後読みの代替 - @yumarule さん)か、あるいは愚直にピリオド以下を取り外して実数部分をカンマ区切りした後に結合させるかのどちらかをしなければいけません。
/*
文字をsplitで配列に変換し、reverseで反転させてjoinで結合する。
すると、Safari・IEでも使える否定先読みで問題を解決できる。
再度反転させればできあがり。
*/
const comma_2 = num => isNaN(num) || String(Number(num)).split('').reverse().join('').replace(/(\d{3})(?!\d*?\.|$)/g, '$1,').split('').reverse().join('');
comma_2(n);
// "123,456,789.01234"
/*
ピリオド以下を一度取り外す
*/
const comma_3 = num => {
var a = String(Number(num)).split('.');
return isNaN(num) || a[0].replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') + (a[1] ? '.' + a[1] : '');
};
comma_3(n);
// "123,456,789.01234"
こんな具合ですね。
Safariでも後読み実装されないかな…。
追記:
@il9437 さんが正規表現ではなくtoLocaleString
で書いたものをコメント欄の方にご提示くださいました。comma_3
のような形式の関数ですが、こちらは非常に直感的で分かりやすいものとなっています。ぜひご確認ください。