/^/

Lesson 21 of 28

Practical Examples

Four complete, real-world regex patterns worked end to end — extracting hashtags, validating slugs, parsing query strings, and redacting card numbers.

7 min read

You’ve learned the individual pieces — literals, classes, quantifiers, groups, anchors, lookaround, flags. Now it’s time to combine them into complete patterns that solve problems you’ll actually run into. Each example below builds up from a plain-English requirement to a working regex.

Extracting hashtags from a tweet

Requirement: pull out every #word from a piece of social text.

A hashtag starts with # followed by one or more “word” characters — letters, digits, or underscores, which is exactly what \w covers.

const tweet = "Loving the #JavaScript and #regex tutorials, #LearnToCode!";
tweet.match(/#\w+/g);
// ["#JavaScript", "#regex", "#LearnToCode"]
Loving the #JavaScript and #regex tutorials, #LearnToCode!
3 matches

The g flag is what makes .match() return every hashtag instead of stopping at the first one. Notice the pattern doesn’t require a space or start-of-string before the # — it just needs \w+ to follow it, which is enough for typical text.

Validating a simple slug

Requirement: confirm a string is a URL-safe slug — lowercase letters, digits, and single hyphens between words, like hello-world-2024.

const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
slugPattern.test("hello-world-2024"); // true
slugPattern.test("Hello-World");      // false — uppercase not allowed
slugPattern.test("hello--world");     // false — double hyphen breaks the pattern
hello-world-2024 Hello-World hello--world

Breaking this down: [a-z0-9]+ matches one chunk of lowercase alphanumerics, and (?:-[a-z0-9]+)* repeats “a hyphen followed by another chunk” zero or more times. The non-capturing group (?:...) groups the repeated unit without creating a capture you don’t need. The anchors ^ and $ make sure the entire string matches this shape — without them, "Hello-World" would still partially match the lowercase portion, which isn’t what a validator should report.

Extracting key=value pairs from a query string

Requirement: turn name=Ada&lang=en&debug=true into structured key/value pairs.

const qs = "name=Ada&lang=en&debug=true";
const pairs = [...qs.matchAll(/(\w+)=(\w+)/g)].map((m) => [m[1], m[2]]);
// [["name", "Ada"], ["lang", "en"], ["debug", "true"]]
name=Ada&lang=en&debug=true
3 matches· capture groups: 2

Here two capture groups do the real work: group 1 grabs everything before the =, group 2 grabs everything after it, and \w+ conveniently stops at the = and & characters since neither is a word character. matchAll (paired with the g flag) returns an iterator of every match with its groups, which is why spreading it into an array and mapping over m[1]/m[2] gives you clean pairs.

Redacting credit card digits with replace

Requirement: replace all but the last four digits of a card number with asterisks before logging it.

const cc = "Card on file: 4111 1111 1111 1234";
cc.replace(/\b(\d{4}) (\d{4}) (\d{4}) (\d{4})\b/, "**** **** **** $4");
// "Card on file: **** **** **** 1234"
Card on file: 4111 1111 1111 1234
1 match· capture groups: 4

This one leans on \b (word boundary) to make sure we’re matching a clean run of digit groups, and four capture groups — one per block of four digits. In the replacement string, $4 refers back to the fourth captured group, so only the last block survives; the rest is replaced with literal asterisks. This is a common real pattern for scrubbing sensitive data out of logs before they’re written to disk.

Tip

Notice a theme across all four examples: anchors (^/$) when you need to validate a whole string, no anchors when you’re extracting matches from inside a larger text, and non-capturing groups ((?:...)) whenever you need repetition but don’t need the matched text back.

Try it yourself

Modify the query-string demo’s pattern to ([\w-]+)=([\w-]+) and add a hyphenated value to the test text, like theme=dark-mode. Predict whether the original pattern or the new one handles it correctly.

What should happen

The original (\w+)=(\w+) stops at the hyphen, because - isn’t a word character — so theme=dark-mode would only capture dark as the value, silently truncating it. Adding - inside the character class ([\w-]+) lets both key and value include hyphens, correctly capturing dark-mode in full. This is a good example of how a pattern that “basically works” can quietly produce wrong results on inputs you didn’t test.

What’s next

Real patterns like these can go wrong in a different way too — not producing the wrong answer, but taking forever to produce any answer. The next lesson covers performance: how backtracking can spiral out of control, and how to write patterns that stay fast.

Quick check

In the hashtag pattern `/#\w+/g`, what does `\w+` capture after the `#`?

In the query string pattern `/(\w+)=(\w+)/g`, what do the two capture groups represent?