Adds a thousand character to a string of numbers

We know that there is a neat way to add a thousandth character to a number as follows:

const numberWithCommas = (x) = > {
  return x.toString().replace(/\B(? =(\d{3})+(? ! \d))/g.",");
}
Copy the code

How does he work? What is the string of things in this re for?

Metacharacters \ B

Let’s break down the main structure of this expression: /\B(? =)/, none of the parts in the expression except \B is a match result, that is, in the whole expression, it is the previous \B that actually participates in the final match to replace with.

If you haven’t worked with \B or \B before, you might wonder why you can use replace to add a comma to a string of numbers. Replace does not mean to replace what? Let’s first look at what the \B metacharacter means.

We may need to take a look at what the opposite of \B stands for: \B stands for Word Boundaries. It matches a position, just as ^ and $represent the beginning and end of a string. What is a character boundary? There are three types of positions that can be called word boundaries:

  • Before the first character of a string. If the character is a word character
  • After the last character of a string. If the character is a word character
  • Between two characters in a string, if one character is a word character and the other is not

For example, \bword\b allows you to match whole words instead of strings like wword1. The word characte mentioned above refers to the characters that make up a word, such as W. Non-word characters are the opposite, such as, and Spaces.

If you still don’t understand, please refer to the explanation here (English). In short, \b matches a position, which is the boundary of a word. Our \B, on the other hand, can be matched by \B that does not belong to \B ‘. For example, the main character of this article, a string of numbers 123, it matches the positions between 1 and 2,2 and 3, that is to say, replace these positions with commas, to implement our thousandles.

Prior assertion (? =)

Our split /\B(? =)/ the second part of (? =) works like this. Take a simple example: x(? =y), matches x if x is followed by y. For example, the x in string xyz will be matched, but the y behind it will not be matched. This is something called antecedent assertion. For more information, see here.

So if we put these two things together, we know that what we’re looking for is a position, and what follows that position has to satisfy certain conditions. Now we can assume, what are the conditions that need to be met? For the familiar thousandles, of course, a comma is added for every three digits from right to left.

So our prior assertion can do one thing first, finding the position of a number followed by a multiple of 3, like this: /\B(? =(\d{3})+)/, the new part is (\d{3})+, where the + sign matches the contents of one or more parentheses, i.e., a string of three digits. For a string of character 12345, this regular expression matches positions 1 and 2,2, and 3, because both positions are followed by multiples of 3 (234 and 345).

We see that there is a general movement towards the final goal. We just need to get rid of some unreasonable positions, such as the one above between 1 and 2.

The following assertion (? ! \d)

The prior assertion guarantees that each position will be followed by a multiple of 3, but not by a multiple of only 3. For example, in the position between 1 and 2 of 12345, in addition to being followed by 234, which meets the condition, there is an extra 5, which is not what we expect. The following assertion addresses this problem.

(understood? After =), for (? !). Should be very easy to understand, is a pair of antonyms. It cannot be followed by the corresponding content. Like here (? ! \ D) No more numbers, such as the extra 5 above. A little bit more, let’s add it up and see :(? =(\d{3})+(? ! \d)) means: it needs to be followed by multiples of 3, and there can be no more digits after that. It could be a decimal point. Or the end of a string. For example, only the positions between 2 and 3 in 12345.67 meet the above conditions. We just have to point out the position and replace it with a comma.

Global substitution /\B(? =(\d{3})+(? ! \d))/g

string.replace(/\B(? =(\d{3})+(? ! \d))/g.",")
Copy the code

I solve the problem by replacing all the places that satisfy this condition with thousandths. I don’t know, does that make sense? If there is any doubt or wrong place, please correct it

Reference Catalogue:

  1. How to print a number with commas as thousands separators in JavaScript
  2. wordboundaries
  3. Regular_Expressions