Joaquin
Joaquin

Reputation: 151

How to make a regular expression that returns false if it doesn't match in js

I am making an app, and I have some inputs, but I only want to accept numbers from 0 to 4, and they can have decimals, but only .5. In other words, 0, 0.5, 1, 1.5, 2, 2.5, ..., 3.5, 4. Only these numbers should return true. However, an if statement like this would be very long, and I thought a regular expression would be far more efficient, but I have no idea how to do this. If anyone could help me, it would be very nice.

Upvotes: 0

Views: 241

Answers (2)

VLAZ
VLAZ

Reputation: 28962

Here are some non-regex solutions:

Arithmetic only

There is a little trick that can be used - the remainder operator % can be used with 0.5 to check if the number is divisible by 0.5. A check can simply be - if it's greater or equal to 0, if it's less or equal to 4 and if it's divisible by 0.5, so it only accepts numbers that have no decimal part (essentially .0) or if the decimal part is .5.

This produces a very concise validation:

const check = x =>
  x >= 0 && x <= 4 && x % 0.5 === 0;

const check = x =>
  x >= 0 && x <= 4 && x % 0.5 === 0;

test(0);
test(0.5);
test(1);
test(1.5);
test(2);
test(2.5);
test(3);
test(3.5);
test(4);

test(0.2)
test(1.25)
test(3.75)
test(4.5);


function test(num) {
  const displayNum = `[${num}]`.padEnd(6, " ");
  console.log(`${displayNum}: ${check(num)}`);
}

This may not work for any divisor! 0.5 is special as it's a power of two (2-1) so it plays nice with the floating point arithmetic. Not all numbers do that:

console.log(30 % 3)
console.log(3 % 0.3)

Using more than arithmetic

You can also get the decimal part by splitting the string representation of the number on .. This allows for easier checking of any decimal part that's not a power of two.

Here is a robust check to allow you to pass any number - whether it's as a string or not.

const check = input => {
  const str = String(input);
  const num = Number(input);
  const decimal = Number(str.split(".")[1]) || 0;
  
  return !Number.isNaN(num) //input is a valid number
    && num >= 0 // more than zero
    && num <= 4 // less than four
    && (decimal === 0 || decimal === 5) //decimal is zero or five
}

const check = input => {
  const str = String(input);
  const num = Number(input);
  const decimal = Number(str.split(".")[1]) || 0;
  
  return !Number.isNaN(num)
    && num >= 0
    && num <= 4
    && (decimal === 0 || decimal === 5)
}

test(0);
test(0.5);
test(".5");
test(1);
test("1.0");
test(1.5);
test(2);
test("2.00");
test(2.5);
test(3);
test(3.5);
test("3.0000");
test(4);
test("4.");

test(0.2)
test(1.25)
test(3.75)
test(4.5);


function test(num) {
  const displayNum = `[${num}]`.padEnd(8, " ");
  console.log(`${displayNum}: ${check(num)}`);
}

If only numbers are expected it can be simplified to :

const check = num => {
  const decimal = Number(String(num).split(".")[1]) || 0;
  
  return !Number.isNaN(num)
    && num >= 0
    && num <= 4
    && (decimal === 0 || decimal === 5)
}

const check = num => {
  const decimal = Number(String(num).split(".")[1]) || 0;
  
  return num >= 0 && num <= 4
    && (decimal === 0 || decimal === 5)
}

test(0);
test(0.5);
test(1);
test(1.5);
test(2);
test(2.5);
test(3);
test(3.5);
test(4);

test(0.2)
test(1.25)
test(3.75)
test(4.5);


function test(num) {
  const displayNum = `[${num}]`.padEnd(8, " ");
  console.log(`${displayNum}: ${check(num)}`);
}

And generalises to

const makeRangeChecker = ({min = 0, max = 100, allowedDecimals = []}) => num => {
  const decimal = Number(String(num).split(".")[1]) || 0;
  
  return num >= min && num <= max
    && allowedDecimals.includes(decimal);
}

const check = makeRangeChecker({min: 0, max: 4, allowedDecimals: [0, 5]});

const makeRangeChecker = ({min = 0, max = 100, allowedDecimals = []}) => num => {
  const decimal = Number(String(num).split(".")[1]) || 0;
  
  return num >= min && num <= max
    && allowedDecimals.includes(decimal);
}

const check = makeRangeChecker({min: 0, max: 4, allowedDecimals: [0, 5]});

test(0);
test(0.5);
test(1);
test(1.5);
test(2);
test(2.5);
test(3);
test(3.5);
test(4);

test(0.2)
test(1.25)
test(3.75)
test(4.5);


function test(num) {
  const displayNum = `[${num}]`.padEnd(8, " ");
  console.log(`${displayNum}: ${check(num)}`);
}

No JavaScript

If you're taking user input from the page, you can just use the normal HTML validation:

<div>Enter a number and change the focus to validate:</div>
<input type="number" step="0.5" min="0" max="4">

Upvotes: 2

Hao Wu
Hao Wu

Reputation: 20669

As VLAZ mentioned, it would be better to use a numeric comparison to do it.

But if you are insist, you could use following regex:

^(?!4\.5)[0-4](?:\.[05])?$

I'm also allowing trailing .0s like 1.0 for better flexibility.

  • ^: the beginning of the string
  • (?!4\.5): 4.5 is not allowed
  • [0-4]: a number from 0 to 4
  • (?:\.[05])?: with an optional decimal point .0 or .5
  • $: end of the string

See the proof

Upvotes: 2

Related Questions