Quiz: Regular Expressions Mastery
Can you tame some wild RegEx?
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?
What matches?
'cat CAT Cat'.match(/cat/g)The i flag makes the pattern case-insensitive:
gfinds all matchesiignores 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.
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.
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
gflag 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.
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).
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.
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”)
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.
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.
What will this return?
'2029-12-31'.match(/(\d{4})-(\d{2})-(\d{2})/).slice(1)The pattern uses three capturing groups:
(\d{4})captures the year(\d{2})captures the month(\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.
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.
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).
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.
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.
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.
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.
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.
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! 💪



