Federico Vicente
Federico Vicente

Reputation: 59

REGEX unique numbers delimited by comma

I am trying to validate a comma separated list of numbers 1-31 unique (not repeating).

i.e.

Upvotes: 3

Views: 1859

Answers (3)

Brian Stephens
Brian Stephens

Reputation: 5271

I totally agree that there are much better ways than regex to look for duplicates, but if you must do this as regex, here's a way (depending on your regex flavor).

See on regex101 (I have made it multiline and extended just for testing and readability).

^
(?!.*\b(\d+)\b.*\b\1\b)
(0?[1-9]|[12][0-9]|3[01])
(,(0?\d|[12][0-9]|3[01]))*
$

Explanation:

  • (?!.*\b(\d+)\b.*\b\1\b) is a negative lookahead to ensure there are no duplicates
  • (0?[1-9]|[12][0-9]|3[01]) matches the first number
  • (,(0?\d|[12][0-9]|3[01]))* matches any more

Note: updated to use word boundaries - based on answer from @sln

Upvotes: 2

Alex Collins
Alex Collins

Reputation: 570

I have created a pattern that can do this.

The pattern:^((?!(\d+),[^\n]*\b\2\b)([1-9]\b|[1-2]\d|3[0-1])(,(?1))?)$

A demo.

A short description.

  • ^ - matches start of a line
  • (?!(\d+),[^\n]*\b\2\b) - Looks ahead to ensure the next number is not repeated
    • (\d+) - grab next number
    • ,[^\n]* - a comma followed by anything but a new line
    • \b\2\b - The next number again repeated
  • ([1-9]\b|[1-2]\d|3[0-1]) - Checks next number between 1-31
    • [1-9]\b - Checks for single digit. Boundary used so to prevent two digit numbers matching.
    • [1-2]\d - Checks for 10-29
    • 3[0-1] - Checks for 30,31
  • (,(?1))?) If followed by comma recurse on main pattern to check if next number is repeated.
  • , - checks followed by acomma
  • (?1) - recurses on main pattern.
  • $ - End of line

Updated: Forgot to check 1-31

Upvotes: 2

user557597
user557597

Reputation:

For a number range that exceeds 1 digit, just add word boundary's around
the capture group and the back reference.
This isolates a complete number.

This particular one is numb range 1-31

 ^                                       # BOS
 (?!                                     # Validate no dups
      .* 
      (                                       # (1 start)
           \b 
           (?: [1-9] | [1-2] \d | 3 [0-1] )        # number range 1-31
           \b 
      )                                       # (1 end)
      .* 
      \b \1 \b 
 )
 (?: [1-9] | [1-2] \d | 3 [0-1] )        # Unrolled-loop, match 1 to many numb's
 (?:                                     # in the number range 1-31
      , 
      (?: [1-9] | [1-2] \d | 3 [0-1] )
 )*
 $                                       # EOS

    var data = [
      '2,4,6,7,1',
      '2,2,6',
      '2,30,16,3',
      '2,',
      '1,2,3,2',
      '1,2,2,3',
      '1,2,3,4,5,6,7,8'
      ];
      
      data.forEach(function(str) {
        document.write(str + ' gives ' + /^(?!.*(\b(?:[1-9]|[1-2]\d|3[0-1])\b).*\b\1\b)(?:[1-9]|[1-2]\d|3[0-1])(?:,(?:[1-9]|[1-2]\d|3[0-1]))*$/.test(str) + '<br/>');
      });

Upvotes: 3

Related Questions