Skip to content Skip to sidebar Skip to footer

Custom Filter With White Space As Thousands Separator Is Not Working

I am trying to write a custom filter to filter numbers on the format xxx xxx,xxx in my application. This is the function I wrote so far: var formatNumber = function(input, fraction

Solution 1:

The problem is here:

output = parts[0].replace(/(\d{3})/g, ""+ thousandsSeparator +"$1").trim();

You are inserting the space before every 3 digits (I'll mark matches between brackets):

"(123)(456)78" --> "( 123)( 456)78"

Then you are trimming it --> "123 45678"

Your idea is nice, but you must do it starting from right to left so I will invert parts[0] and the replacement:

parts[0] = parts[0].split('').reverse().join('');
"12345678" --> "87654321"

parts[0] = parts[0].replace(/(\d{3})/g, "$1" + thousandsSeparator).trim();
"(876)(543)21" --> "(876 )(543 )21"

output = parts[0].split('').reverse().join('');
"876 543 21" --> "12 345 678"

Which is the output you want. Here is the updated snippet:

var formatNumber = function(input, fractionSeparator, thousandsSeparator, fractionSize) {
  fractionSeparator = fractionSeparator || ',';
  thousandsSeparator = thousandsSeparator || ' ';
  fractionSize = fractionSize || 3;
  var output = '',
    parts = [];

  input = input.toString();
  parts = input.split(".");
  
  parts[0] = parts[0].split('').reverse().join('');
  parts[0] = parts[0].replace(/(\d{3})/g, "$1" + thousandsSeparator).trim();
  output = parts[0].split('').reverse().join('');
  
  if (parts.length > 1) {
    output += fractionSeparator;
    output += parts[1].substring(0, fractionSize);
  }
  return output;
};

console.log(formatNumber(154455451.58));

console.log(formatNumber(55451.58));
console.log(formatNumber(54455451.58));

Solution 2:

You can split your number on . and then split it and convert it to an array and can use array#map and array#filter to get a new array with its element grouped in chunk of 3 and then you just have to reverse and join it backl

constformatNumber = (num, fractionSeparator, thousandsSeparator, fractionSize) => {
  fractionSeparator = fractionSeparator || ',';
  thousandsSeparator = thousandsSeparator || ' ';
  fractionSize = fractionSize || 3;
  let [first, second] = num.toString().split('.');
  let reversed = first.split('').reverse();
  reversed = reversed.map((e,i) => i%fractionSize===0 ? reversed.slice(i,i+fractionSize).join('') :null)
          .filter(x => x)
          .reverse()
          .join(thousandsSeparator);
  return reversed + fractionSeparator + second;
}

console.log(formatNumber(154455451.58));
console.log(formatNumber(55451.58));
console.log(formatNumber(54455451.58));

Solution 3:

Here's another approach that is based off of a "currency formatter" method that I once wrote:

var sRegexFormatPattern = '^([^\\' + thousandsSeparator +
                          '\.]+)(\\d{3}[\\' + thousandsSeparator +
                          '\.]|\\d{3}$)';
var regexFormatPattern = newRegExp(sRegexFormatPattern);

while (regexFormatPattern.test(input)) {
    input = input.replace(regexFormatPattern, '$1' + thousandsSeparator + '$2');
}

Let's break down that regex starting from back to front . . .

  • The second capture group ((\\d{3}[\\' + thousandsSeparator + '\.]|\\d{3}$)) matches exactly 3 digits and a either a decimal point (\d{3}\.), the character passed in as the "thousandsSeparator" (\d{3}\THOUSANDS_SEPARATOR), or the end of the string (\d{3}$).

  • The first capture group (^([^\\' + thousandsSeparator + '\.]+)) matches all characters (which should all be digits . . . you'd need a separate check to make sure of that) that aren't either a decimal point or the character passed in as the "thousandsSeparator", up until it hits the second capture group.

NOTE: I added in an escape slash before the "thousandsSeparator", just in case somebody typed in something that has another meaning in regex, but, even still, some characters could cause issues in the execution of the regex . . . would need to look into it more to make it foolproof.

As long as it can find those two pieces inside the value, it will break them up with the "thousandsSeparator" and check again. Using "12345678.90" as an example, the iterations go like this:

  • 12345678. --- changes the number to ---> 12345 678.90
  • 12345 678. --- changes the number to ---> 12 345 678.90

Everything before and after that section is more-or-less what you already had . . . I added a minor tweak to how you formatted the "fraction" part of the string as well, but the rest is pretty much the same (see full JS code below).

Using your test values, I got:

- 451.585478 --->           451,585- 154455451.58 ---> 154 455 451,58- 54455451.58 --->   54 455 451,58- 55451.58 --->          55 451,58

DEMO

$("document").ready(function() {
  $(".formattingInput").on("change", function() {
    $("#formattedNumber").text(formatNumber(
      $("#unformattedNumberInput").val(),
      $("#fractionSeparator").val(),
      $("#thousandsSeparator").val(),
      $("#fractionSize").val()
    ));
  });
});

var formatNumber = function(input, fractionSeparator, thousandsSeparator, fractionSize) {
  fractionSeparator = fractionSeparator || ',';
  thousandsSeparator = thousandsSeparator || ' ';
  fractionSize = fractionSize || 3;

  var sRegexFormatPattern = '^([^\\' + thousandsSeparator +
    '\.]+)(\\d{3}[\\' + thousandsSeparator +
    '\.]|\\d{3}$)';
  var regexFormatPattern = newRegExp(sRegexFormatPattern);

  while (regexFormatPattern.test(input)) {
    input = input.replace(regexFormatPattern, '$1' + thousandsSeparator + '$2');
  }

  var parts = input.split('.');

  if (parts.length > 1) {
    input = parts[0] +
      fractionSeparator +
      parts[1].substring(0, fractionSize);
  }

  return (input.length > 0) ? input : 'Please input a value in the "Number" field.';
};
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div><labelfor="unformattedNumberInput">Number: <inputtype="text"id="unformattedNumberInput"class="formattingInput" /></label><br /><labelfor="thousandsSeparator">Thousands Separator: <inputtype="text"id="thousandsSeparator"class="formattingInput" /></label><br /><labelfor="fractionSeparator">Fraction Separator: <inputtype="text"id="fractionSeparator"class="formattingInput" /></label><br /><labelfor="fractionSize">Fraction Size: <inputtype="text"id="fractionSize"class="formattingInput" /></label><br /><br /></div><div><strong>Result:</strong><spanid="formattedNumber">Please input a value in the "Number" field.</span></div>

Post a Comment for "Custom Filter With White Space As Thousands Separator Is Not Working"