Regex that allows PascalCased with one or more consecutive uppercase letters and numbers

Viewed 71

I'm trying to check user input for PascalCased names and this works, but I would like to also allow one or more consecutive upperCase letters eg. UNOrganization and also allow numbers in between eg. A2BOrganization.

So all of the following should be allowed: ABCWord, A3BWord, OtherWord, Word3, Word3AB (last unlikely but if possible fine)

  if (value.match(/^[A-Z][a-z]+(?:[A-Z][a-z]+)*$/)) {
    //logic here
  }

Regex is a little beyond me and the logic to parse the strings would be too long for my needs and I know this can be done in a one-liner with regex so hopefully someone more savvy can help me.

3 Answers

Use

^[A-Z]+[a-z]*(?:\d*(?:[A-Z]+[a-z]*)?)*$

See proof

If you require at least one lowercase letter in the input string:

^(?=.*[a-z])[A-Z]+[a-z]*(?:\d*(?:[A-Z]+[a-z]*)?)*$

Explanation

--------------------------------------------------------------------------------
  ^                        the beginning of the string
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    .*                       any character except \n (0 or more times
                         (matching the most amount possible))
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of look-ahead
--------------------------------------------------------------------------------
  [A-Z]+                   any character of: 'A' to 'Z' (1 or more
                           times (matching the most amount possible))
--------------------------------------------------------------------------------
  [a-z]*                   any character of: 'a' to 'z' (0 or more
                           times (matching the most amount possible))
--------------------------------------------------------------------------------
  (?:                      group, but do not capture (0 or more times
                           (matching the most amount possible)):
--------------------------------------------------------------------------------
    \d*                      digits (0-9) (0 or more times (matching
                             the most amount possible))
--------------------------------------------------------------------------------
    (?:                      group, but do not capture (optional
                             (matching the most amount possible)):
--------------------------------------------------------------------------------
      [A-Z]+                   any character of: 'A' to 'Z' (1 or
                               more times (matching the most amount
                               possible))
--------------------------------------------------------------------------------
      [a-z]*                   any character of: 'a' to 'z' (0 or
                               more times (matching the most amount
                               possible))
--------------------------------------------------------------------------------
    )?                       end of grouping
--------------------------------------------------------------------------------
  )*                       end of grouping
--------------------------------------------------------------------------------
  $                        before an optional \n, and the end of the
                           string
/^(?=.*[a-z])[A-Z]\d*[a-z]*(([A-Z\d]*[A-Z]|[A-Z][A-Z\d]*|\d+$)[a-z]*)*$/

seems to fit your requirements. See tests below.

The idea is to start with a capital letter followed by an optional digit and zero or more lowercase letters. Then enter the main group repeated zero or more times. This group matches one or more capital letters or digits followed by zero or more lowercase letters. The alternation handles allowing digits at the end and disallowing lone digits sandwiched between two lowercase characters.

const pat = /^(?=.*[a-z])[A-Z]\d*[a-z]*(([A-Z\d]*[A-Z]|[A-Z][A-Z\d]*|\d+$)[a-z]*)*$/;

const tests = [
  "UNOrganization",
  "A2BOrganization", 
  "ABCWord", 
  "A3BWord", 
  "OtherWord", 
  "Word3", 
  "Word3AB",
  "a",
  "A",
  "Aa",
  "aA",
  "AAA",
  "aaa",
  "9A",
  "A4",
  "A4a",
  "AaA",
  "AaAa",
  "Aa1",
  "AaA1",
  "Aa1a",
  "",
];
const len = 2 + Math.max(...tests.map(e => e.length));
tests.forEach(e => 
  console.log(`${`'${e}'`.padStart(len)} => ${pat.test(e)}`)
);

Another pattern:

^[A-Z]+[A-Za-z]*(?:[0-9]+(?:[A-Z]+[A-Za-z]*)*)*$
  • [A-Z]+[A-Za-z]*: At least one capital letter optionally followed a sequence of letters of any case, ...

  • (?: ... )*: ... optionally followed by a sequence of :

    • [0-9]+: one or more digit...

    • (?: ... )*: optionally followed by a sequence of :

      • [A-Z]+[A-Za-z]*: at least one capital letter optionally followed a sequence of letters of any case

Regex101.com working examples (replaced ^ and $ by word delimiters \b).

Related