Lesson 17 of 18 · 0 of 2 exercises solved
Regular Expressions
You will learn how to describe text patterns with and use them to validate, extract and replace text. One pattern can replace many lines of manual handling.
// Two ways to build one
const re1 = /\d{3}-\d{4}/ // literal — compiled once, use when the pattern is fixed
const re2 = new RegExp('\\d{3}-\\d{4}') // from a string — note the doubled backslashes
// The pieces you will actually use
// \d digit \w word char \s whitespace . any char
// + 1+ * 0+ ? 0 or 1 {2,5} between 2 and 5
// ^ start $ end | or [abc] one of [^abc] none of
// () capture group (?:) group without capturing
const text = 'Order 1234 shipped on 2026-08-08 to alice@example.com'
console.log(/\d+/.test(text)) // true
console.log(text.match(/\d+/g)) // ['1234', '2026', '08', '08']
console.log(text.search(/@/)) // index of first match
console.log(text.replace(/\d{4}-\d{2}-\d{2}/, 'DATE'))
// Capture groups pull pieces out; named groups make them readable
const dateRe = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const m = '2026-08-08'.match(dateRe)
console.log(m.groups.year, m.groups.month) // 2026 08
// matchAll gives every match WITH its groups
const log = 'GET /home 200\nPOST /login 401\nGET /admin 403'
const lineRe = /(?<method>GET|POST) (?<path>\/\S*) (?<status>\d{3})/g
for (const { groups } of log.matchAll(lineRe)) {
if (groups.status !== '200') console.log(`${groups.method} ${groups.path} failed`)
}
// Replace with a function for full control
const prices = 'shirt $20, shoes $75'
console.log(prices.replace(/\$(\d+)/g, (_, n) => '$' + (Number(n) * 1.1).toFixed(2)))
// Flags: g global, i ignore case, m multiline (^ and $ per line), s dot matches newline
console.log('Hello HELLO'.match(/hello/gi)) // ['Hello', 'HELLO']
// Practical patterns
const patterns = {
// "Good enough" email check — real validation means sending a confirmation mail
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
slug: /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
hexColor: /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i,
// Lookahead: at least one digit, one lowercase, one uppercase, 8+ chars
strongPassword: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/,
}
console.log(patterns.slug.test('my-first-post')) // true
console.log(patterns.hexColor.test('#1a2b3c')) // true
// Splitting on a pattern
console.log('a1b22c333d'.split(/\d+/)) // ['a','b','c','d']
// Escaping user input before putting it in a regex
function escapeRegex(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
const needle = new RegExp(escapeRegex('price (USD)'), 'i')
console.log(needle.test('Total price (USD): 40')) // true
Practice
Write isValidEmail(s) with a simple pattern (something@something.tld, no spaces) and extractHashtags(text) that returns every #word in the text without the hash.
Exercise
4 checksTest and extract
function isValidEmail(s) {
return false
}
function extractHashtags(text) {
return []
}
console.log(isValidEmail('sam@mail.co.uk'), isValidEmail('not an email'))
console.log(extractHashtags('Learning #python and #sql today #100DaysOfCode'))
Press Run to try your code, then Check answer to grade it.Write slugify(title): lower-case, replace every run of non-alphanumeric characters with a single hyphen, and trim hyphens from the ends. "Hello, World! 2026" becomes "hello-world-2026".
Exercise
3 checksSlugify
function slugify(title) {
return title
}
console.log(slugify('Hello, World! 2026'))
Press Run to try your code, then Check answer to grade it.Check yourself
What does the g flag change in "a1b2".replace(/\d/, "#")?
Finished reading?2 exercises still open. You can come back to them later.
Tip: use the ← and → keys to move between lessons.