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?
Warmup
What matches?
'cat CAT Cat'.match(/cat/g)
The i
flag makes the pattern case-insensitive:
g
finds all matchesi
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.
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.
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.
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).
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.
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”)
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.
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.
Basic Matching
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.
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.
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).
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.
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.
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.
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.
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.
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! 💪