Dan Levy's Avatar DanLevy.net

Quiz: Regular Expressions Mastery

Can you tame some wild RegEx?

Quiz: Regular Expressions Mastery

Ready to wrestle with some Regular Expressions? 🤼‍♂️

Test your RegEx knowledge with questions covering basic patterns, quantifiers, groups, and those tricky look-around assertions. From simple string matching to complex pattern validation - can you spot the correct regex?

1. 

Warmup

What matches?

'cat CAT Cat'.match(/cat/g)

The i flag makes the pattern case-insensitive:

  • g finds all matches
  • i ignores case differences

Without the i flag, only lowercase “cat” matches.

This is particularly useful when dealing with user input or HTML where case might vary.

Learn more about RegExp flags

2. 

Warmup

What will this code return?

const words = ['cat', 'hat', 'what', 'bat'];
words.filter(word => word.match(/^[ch]at/))

The pattern /^[ch]at/ matches strings that:

  • Start (^) with either ‘c’ or ‘h’ (that’s what [ch] means - a character class matching one character)
  • Followed by literally ‘at’

Therefore, only “cat” and “hat” match this pattern. The filter() method keeps only the matching elements.

Learn more about character classes on MDN

3. 

Basic Matching

What will this match?

'<div>Hello</div><div>World</div>'.match(/<div>.*?<\/div>/g)

The pattern /<div>.*?<\/div>/g uses non-greedy matching with *? which means:

  • Match <div>
  • Match any character (.*) but as few as possible (?)
  • Until finding </div>
  • The g flag makes it match all occurrences

Without the ?, the greedy .* would match everything from the first <div> to the last </div>, giving one big match. With ?, it matches each pair separately.

Learn more about greedy vs lazy matching

4. 

Common Gotchas

What will this return?

'hello\nworld'.match(/\w+/g)

The \w+ pattern matches one or more word characters. Even though there’s a newline in the string, \w matches:

  • Letters (a-z, A-Z)
  • Numbers (0-9)
  • Underscore (_)

So, the newline acts as a word boundary, and we get two matches. If we had used .*, it wouldn’t match the newline by default (you’d need the s flag for that).

Learn more about metacharacters

5. 

Look-ahead

What will this match?

'$100 and €50'.match(/\d+(?=[\$€])/g)

This pattern won’t match anything because the look-ahead is backwards! The correct pattern would be /(?=[\$€])\d+/g.

Look-aheads check what comes after the current position. The pattern as written looks for:

  • One or more digits (\d+)
  • Followed by ((?=...)) either $ or € ([\$€])

Since there are no numbers followed by currency symbols (they’re preceded by them), we get no matches.

Learn more about look-ahead assertions

6. 

Basic Matching

What will match?

'cat cats category'.match(/\bcat\b/g)

The \b represents a word boundary, which matches:

  • Between a word character and a non-word character
  • At the start/end of the string if there’s a word character

So /\bcat\b/ matches “cat” only when it’s a complete word, not part of another word.

  • ✅ “cat” (surrounded by spaces)
  • ❌ “cats” (no boundary after “cat”)
  • ❌ “category” (no boundary after “cat”)

Learn more about word boundaries

7. 

Common Gotchas

What’s the output?

'banana'.match(/a/g)

The g (global) flag changes how match() behaves:

  • Without g: Returns first match with capture groups
  • With g: Returns array of all matching strings

In this case, it finds all occurrences of “a” in “banana”.

Note: If you need both all matches AND capture groups, use matchAll() or the exec() method in a loop.

Learn more about the global flag

8. 

Look-behind

What matches this pattern?

'abc123 def456'.match(/(?<!abc)\d+/g)

The negative look-behind (?<!abc) ensures that the digits aren’t preceded by “abc”:

  • ❌ “123” (preceded by “abc”)
  • ✅ “23” (preceded by “abc1”)
  • ✅ “456” (preceded by “def”)

Look-behinds must be fixed-length in most browsers (no * or +).

Note: Look-behind support is relatively recent in JavaScript. Check browser compatibility if you need to support older browsers.

9. 

Basic Matching

What will this return?

'2029-12-31'.match(/(\d{4})-(\d{2})-(\d{2})/).slice(1)

The pattern uses three capturing groups:

  1. (\d{4}) captures the year
  2. (\d{2}) captures the month
  3. (\d{2}) captures the day

match() without the g flag returns:

  • Index 0: Full match
  • Index 1+: Capture groups

slice(1) is a common trick to get just the capture groups.

Learn more about groups and capturing

10. 

Look-ahead

Which will be the result of this?

/^\d+(?![a-z])/ig.match("123aBc")

The negative look-ahead (?![a-z]) ensures there are no lowercase letters after the digits. Because the “3aBc” part has a lowercase letter after the digits, its portion doesn’t match. So only the beginning “12” matches.

Learn more about negative look-ahead

11. 

Look-behind

What’s returned?

'a,b,c'.split(/(?<=,)/)

The pattern /(?<=,)/ is a look-behind that matches after a comma:

  • a, (after comma)
  • b, (after comma)
  • c (no comma after)

The look-behind doesn’t consume the comma, so it’s not part of the split result.

This is useful when you want to split a string based on what comes before it without losing the split character(s).

Learn more about look-behind assertions

12. 

Common Gotchas

What matches?

'$100'.match(/$\d+/)

Special characters need escaping with \ to match literally:

  • $ is a special character (end of string)
  • To match a literal dollar sign, escape it: \$

Common characters needing escape:

. * + ? ^ $ [ ] \ ( ) { } |

Without escaping, many special characters have regex meanings that might not be what you want.

Learn more about escaping special characters

13. 

Look-behind

What’s matched?

'$100'.match(/(?<=\$)\d+/)

The positive look-behind (?<=\$) ensures the digits are preceded by a dollar sign:

  • (?<=\$): Look-behind for dollar sign
  • \d+: Match one or more digits

Look-behind assertions don’t consume characters; they only check what comes before. This is useful when you want to match something based on what comes before it without including the preceding part.

Learn more about look-behind assertions

14. 

Basic Matching

What’s matched?

'<b>bold</b>'.match(/<b>(.*?)<\/b>/).slice(1)

The pattern uses lazy matching with *?:

  • <b>: Match opening tag
  • (.*?): Capture any chars (lazy)
  • </b>: Match closing tag

The ? after * makes it lazy, matching as few characters as possible. Without ?, it would be greedy and match as much as possible.

slice(1) returns just the captured group.

Learn more about greedy vs lazy matching

15. 

Basic Matching

What matches?

'😀 🙂'.match(/\p{Emoji}/gu)

The u flag enables:

  • Unicode property escapes (\p{...})
  • Correct handling of surrogate pairs

Without u, emoji and other Unicode characters might not match correctly. The pattern \p{Emoji} matches any emoji character.

Note: Unicode property escapes require the u flag.

Learn more about Unicode mode

16. 

Look-into-hell

Apologies in advance! 😈
Which password matches this pattern?

/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/

Don’t write anything like this in production! 😅

This pattern uses multiple positive look-aheads to enforce:

  • At least one uppercase letter: (?=.*[A-Z])
  • At least one lowercase letter: (?=.*[a-z])
  • At least one digit: (?=.*\d)
  • At least one special character: (?=.*[!@#$%^&*])
  • Minimum length of 8: .{8,}

Look-aheads are perfect for password validation because they can check for multiple criteria without consuming characters.

Learn more about password validation patterns

Quiz Score:

Congrats! Quiz completed.

How did you do? 🧐

Regular Expressions can be a beast to tame, but they’re incredibly powerful once you get the hang of them (and all the newer syntax). Keep practicing, and you’ll be a RegEx master in no time! 🧙‍♂️

Looking for a break after all that RegEx?
Pftt, remember: break after skills!

Hit my gym to crush some more challenges! 💪

Edit on GitHubGitHub