Reputation: 59
I am trying to validate a comma separated list of numbers 1-31 unique (not repeating).
i.e.
2,4,6,7,1 is valid input.
2,2,6 is invalid
2 is valid
2, is invalid
1,2,3,4,15,6,7,31 is valid
1,2,3,4,15,6,7,32 is invalid
20,15,3
I tried
^((([0]?[1-9])|([1-2][0-9])|(3[01]))(?!([0]?[1-9])|([1-2][0-9])|(3[01])*,\\1(?!([0]?[1-9])|([1-2][0-9])|(3[01]))
but it's accepting repeating numbers
Upvotes: 3
Views: 1859
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 moreNote: updated to use word boundaries - based on answer from @sln
Upvotes: 2
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-293[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
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