Reputation: 45
I want to put my product filter in my URL, but to match filter words:
Expected match:
filter:city:cityname-type:something-type2:something123-price:1000
filter:city:cityname-type2:something-type:something123-price:1000
filter:city:cityname-price:1000
To be more precise: string starts with "filter:" word, and after it, at least one filter must be present. Each filter is name:value
First filter (in this case "city") is mandatory, others are not, also order for others can be changed (city,type,type2 OR city,type2,type)
. Expected filters are at least 1 - 20.
This is regex I am using
^filter:((city:([a-z0-9]+)){1}((-type:([a-z0-9]+))|(-type2:([a-z0-9]+))|(-price:([0-9]+)))*)
How can I match each used filter only once, because with this regex above it will match:
filter:city:some-price:123-type2:aaa-type2:bbb
(type2 is used more than once). And if there is better regex for this please post.
Upvotes: 2
Views: 103
Reputation: 626738
You can use
^(?!.*[-:](\b(?:city|type2?|price):).*\1)filter:((city:([a-z0-9]+))((-type:([a-z0-9]+))|(-type2:([a-z0-9]+))|(-price:([0-9]+)))*)\s*$
See the regex demo
The lookahead (?!.*[-:](\b(?:city|type2?|price):).*\1)
will check (right at the beginning of a string) and fail the match if a string contains (somewhere inside) -
or :
followed with whole words (as \b
is a word boundary) city
, or type
, or type2
, or price
followed with :
(this key is captured into Group 1 with the (...)
) and then some 0+ characters (.*
) and then the same captured key (as \1
is a backreference to the captured value).
The \s*$
will allow trailing whitespace and the end-of-string and will disallow unknown filters.
To shorten the pattern, in PCRE/Boost, you can use
^(?!.*[-:](\b(?:city|type2?|price):).*\1)filter:(city|type2?|price):([a-z0-9]+)(?:-(?2):(?3))*\s*$
See regex demo (in Ruby, \g<1>
and \g<2>
can be used instead of (?1)
and (?2)
).
If the regex flavor is different and does not allow recursion, use
^(?!.*[-:](\b(?:city|type2?|price):).*\1)filter:(?:city|type2?|price):[a-z0-9]+(?:-(?:city|type2?|price):[a-z0-9]+)*$
See another demo
Upvotes: 1