skip to Main Content

I’m trying to write a Regex in Ruby for a shipping query.

If postcodes match MK1 – MK10, MK19, MK43, MK46 or MK77, then allow it.
If postcodes match NN1 – NN7, NN12, NN13, NN29 or NN77, then allow it.
If postcodes match MK11 – MK18 then don’t allow it.

My trouble is that in the UK our postcodes are a bit funny where you can put MK1 1TS and MK11TS and they’re considered the same. By not allowing MK11, MK11TY could be misread as MK11.

I’ve written a regex below, and so far it will disallow MK111TS and MK11s1TS, and allow MK1s1TS but not MK11TS. Any help would be greatly appreciated, I’ve only tested this for MK11 so far.

^((?!MK11d).)*$&^((?!MK11sd).)*$|(MK(1 |2 |3 |4 |5 |6 |7 |8 |9 |10 ))|(MK19)|(MK43)|(MK46)|(MK77)|(NN1)|(NN2)|(NN3)|(NN4)|(NN5)|(NN6)|(NN7)|(NN12)|(NN13)|(NN29)|(NN77)

Thanks in advance.

2

Answers


  1. Chosen as BEST ANSWER

    Now I have written the following to match the postcodes format exactly:

    #format: Area Code, Localities accepted, whitespace (MKor not), any digit, any single character, any single character
        ((MK|mk|Mk|mK)(?:1|2|3|4|5|6|7|8|9|10|19|43|46|77)sd[A-Za-z][A-Za-z]) #with whitespace
        |
        ((MK|mk|Mk|mK)(?:1|2|3|4|5|6|7|8|9|10|19|43|46|77)d[A-Za-z][A-Za-z]) #without whitespace
        |
        ((NN|nn|Nn|nN)(?:1|2|3|4|5|6|7|12|13|29|77)sd[A-Za-z][A-Za-z]) #with whitespace
        |
        ((NN|nn|Nn|nN)(?:1|2|3|4|5|6|7|12|13|29|77)d[A-Za-z][A-Za-z]) #without whitespace
    

    This works for my purposes, I got here using Cary's answer, which has been extremely helpful. Thank you and have marked up.


  2. r = /
        (?:                                     # begin non-capture group           
          MK                                    # match characters
          (?:1|2|3|4|5|6|7|8|9|10|19|43|46|77)  # match one of the choices
          |                                     # or
          NN                                    # match characters
          (?:1|2|3|4|5|6|7|12|13|29|77)         # match one of the choices
        )                                       # end non-capture group
        (?![^sA-Z])                            # do not match a space or cap letter
        /ix                                     # case indifferent and free-spacing
                                                # regex definition mode
    

    This is conventionally written

    r = /(?:MK(?:1|2|...|10|19|...|77)|NN(?:1|2|...|7|12|13|29|77))(?![^sA-Z])/i
    
    "MK4 abc def MK11MK19ghi NN6 jkl NN13 NN29NN77".scan(r)
       # => ["MK4", "NN6", "NN13", "NN29", "NN77"]
    

    "MK11" is not matched because "11" is not in the list. "MK19" is not matched because it is followed by a character that is neither a space nor a capital letter.

    Alternatively, one could write

    s = (['MK'].product(%w{1 2 3 4 5 6 7 8 9 10 19 43 46 77}).map(&:join) +
         ['NN'].product(%w{1 2 3 4 5 6 7 12 13 29 77}).map(&:join)).join('|')
      # => "MK1|MK2|...|MK10|MK19|MK43|MK46|MK77|NN1|NN2|...|NN7|NN12|NN13|NN29|NN77"
    r = /(?:#{s})(?![^sA-Z])/i
      #=> /(?:MK1|MK2|...|MK10|MK19|...|MK77|NN1|NN2|...|NN7|NN12|NN13|NN29|NN77)(?![^sA-Z])/
    

    If the remainder of the postal code is to be included in the regex, perhaps something like the following could be done.

    suffixes = %w|ES AB CD EF|.join('|')
      #=> "ES|AB|CD|EF"
    

    Then replace (?![^sA-Z])/x with the following.

    s?             # optionally match a space
    (?:#{suffixes}) # match a valid suffix in a non-capture group
    (?!S)          # do not match a non-whitespace char (negative lookahead)
    /ix             # case-indifferent and free-spacing regex definition mode
    

    Note the negative lookahead is satisfied if the suffix is at the end of the string.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search