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'))
 
JavaScript sandboxCtrl+Enter runs
// 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']
 
JavaScript sandboxCtrl+Enter runs
// 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
 
JavaScript sandboxCtrl+Enter runs

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

Test and extract

4 checks
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'))
 
JavaScript sandboxCtrl+Enter runs
Output
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

Slugify

3 checks
function slugify(title) {
  return title
}

console.log(slugify('Hello, World! 2026'))
 
JavaScript sandboxCtrl+Enter runs
Output
Press Run to try your code, then Check answer to grade it.

Check yourself

Quick check

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.