preface

In scenarios involving large numbers, such as number of people, amount of money, etc., we often need to place the numbers in thousandths and separate them with commas, which is easier to read.

Such as

123456789
Copy the code

If it’s in the thousandths

123.456.789
Copy the code

Comparing these two kinds, we must choose the second one, which is convenient to read.

implementation

So how do you do that?

Typically, regular expressions are used

function formatNumber(num) {
  const reg = /(\d)(? =(\d{3})+$)/g
  return num.toString().replace(reg, '$1')
}
formatNumber(12345678) / / "12345678"
formatNumber(8765432188) / / "8765432188"
Copy the code

Test passed.

But this is based on integers. If your number has a decimal point, the result is as follows

formatNumber(1234.5678) / / 1234.5, "678"
Copy the code

The expectation is “1,234.5678”, you don’t need to do thousandths of the decimal part, you just need to do thousandths of the integer part,

Actual is 1234.5 “, 678”

So you need to optimize the regex.

function formatNumber(num) {
  const reg = /(\d)(? =(\d{3})+\.) /g
  return num.toString().replace(reg, '$1')
}
formatNumber(1234.5678) / / "1234567"
formatNumber(1234) / / "1234"
Copy the code

As you can see from the example above,

It works if the number has a decimal point

However, if the number has no decimal point, this re will not work, so you need to combine the two re’s to support integers and decimals

function formatNumber(num) {
  num = num.toString()
  let reg = num.indexOf('. ') > -1 ? /(\d)(? =(\d{3})+\.) /g : /(\d)(? =(\d{3})+$)/g
  return num.replace(reg, '$1')
}
formatNumber(1234.5678) / / "1234567"
formatNumber(1234) // "1,234"
Copy the code

The following is the regular /(\d)(? = (\ d {3}) + $)/g and/(\ d)? =(\d{3})+\.) /g for analysis:

  1. This regular is mainly used to exploitd{3}Every three digits are matched, followed by a comma, and so on.
  2. The re is added here$and.In order to give priority to the following three bits, if the beginning is less than three bits, it will not be processed.
  3. A regular expression is also used here(? =pattern), the term is calledZero width assertion, or also calledPositive positive pre-check.

Implement thousandths of separation, the core point is this, let’s talk about the regular expression.

Zero width assertion (positive affirmative precheck)

(? =pattern), which matches the regular expression but does not get the match. The next match will start at that location.

It will be easier to understand with examples:

var regA = / the answer? =cp3)/
console.log('the answer cp3'.match(regA)) // [" answer "]

var regB = /cp3(? =cp3)/g
console.log('cp3cp3cp3cp3'.match(regB)) // ["cp3", "cp3", "cp3"]
Copy the code

The first example shows that the answer cp3 is matched, but only the answer is retrieved, not cp3

In the second example, you can see that cp3cp3 is matched, and only CP3 is retrieved. The next match starts with the second cp3, and so on, and finally outputs three CP3.

Combined with the thousandth separation example above, because we used the zero-width assertion, since it will start at that position again next time, this results in every number being matched up to the end

let reg = /(\d)(? =(\d{3})+)/g
'1234567890'.replace(reg, (match,$1, $2, index, str) = > {
 console.log(match,$1, $2, index, str)
 return $1 + ', '}) output1 1 890 0 1234567890The output2 2 678 1 1234567890The output3 3 789 2 1234567890The output4 4 890 3 1234567890The output5 5 678 4 1234567890The output6 6 789 5 1234567890The output7 7 890 6 1234567890return"1,2,3,4,5,6,7,890"
Copy the code

So we add $or to the zero width assertion. To constrain, restrict the preference to match the end, the beginning less than 3 bits will not be processed

let reg = /(\d)(? =(\d{3})+$)/g
'1234567890'.replace(reg, (match,$1, $2, index, str) = > {
 console.log(match,$1, $2, index, str)
 return $1 + ', '}) output1 1 890 0 1234567890The output4 4 890 3 1234567890The output7 7 890 6 1234567890return"1234567890"
Copy the code

There will be no problem in that case.

conclusion

The above is the use of re to achieve a thousand – digit separation summary

Complete code:

function formatNumber(num) {
  num = num.toString()
  let reg = num.indexOf('. ') > -1 ? /(\d)(? =(\d{3})+\.) /g : /(\d)(? =(\d{3})+$)/g
  return num.replace(reg, '$1')}Copy the code