iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Alternation

Alternation (the | operator) matches one option or another. Used everywhere — routing rules, file extensions, ICD codes — but it’s the regex feature most prone to wrong-precedence bugs and catastrophic backtracking when overused.

Grouping, anchors, order, performance

EXAMPLE
// 1) Basic alternation
/cat|dog/.test('I have a cat');               // true
/cat|dog/.test('I have a fish');              // false

// 2) Precedence — | has the LOWEST precedence
// /^cat|dog$/  means  /(^cat)|(dog$)/
//   matches 'cat...' at the start OR '...dog' at the end
// Often NOT what you wanted. Use a group:
/^(cat|dog)$/.test('cat');                    // true
/^(cat|dog)$/.test('cats');                   // false

// 3) Real-world: file extensions
const img = /\.(jpe?g|png|gif|webp|avif|svg)$/i;
img.test('photo.JPG');                         // true
img.test('logo.svg');                          // true
img.test('archive.tar.gz');                    // false

// 4) Real-world: HTTP methods
const write = /^(POST|PUT|PATCH|DELETE)$/;
write.test('POST');                            // true
write.test('GET');                             // false

// 5) Order matters — first match wins, left to right
/Java|JavaScript/.exec('JavaScript');
// [ 'Java', index: 0 ]   ← matched 'Java' before trying 'JavaScript'

// Put LONGER alternatives FIRST when one is a prefix of another
/JavaScript|Java/.exec('JavaScript');
// [ 'JavaScript', index: 0 ]

// 6) Anchors apply to the whole alternation when grouped
/^foo|bar$/.test('xbar');                     // true   — bar matches at end
/^(foo|bar)$/.test('xbar');                   // false  — anchored as a whole

// 7) Non-capturing groups — when you don't need the value
/^(?:cat|dog) food$/.test('cat food');         // true; (?:…) doesn't fill match[1]

// 8) Performance — alternation backtracks
// Each alternative is tried in order. With many alts and shared prefixes,
// the engine retries from the start every time.

// Bad: redundant prefixes
/cat|car|can|cap|cab/
// Better: factor the common prefix
/ca[trnpb]/
// or
/ca(?:t|r|n|p|b)/

// 9) Catastrophic backtracking — alternation inside repetition
// /^(a|a)*$/.test('a'.repeat(30) + '!')
// is exponential. ALWAYS make alternatives disjoint inside *, +, etc.
// Modern engines (RE2, Rust regex) avoid this. JS, Python re do not by default.

// 10) Character classes are not alternation
/[abc]/.test('a');                            // matches a, b, or c — one char
/a|b|c/.test('a');                            // same result, but slower
// Use [...] for single-char alternatives; reserve | for multi-char.

// 11) Word boundaries with alternation
/\b(cat|dog)\b/g                              // whole words only
'cathedral and catalog'.match(/\b(cat|dog)\b/g);   // null — no whole word 'cat'
'a cat and dog'.match(/\b(cat|dog)\b/g);         // ['cat', 'dog']

// 12) Branch-reset / DEFINE — advanced engines only
// PCRE supports (?| … | … ) for shared capture numbering. JS / Python lack this.

// 13) Real-world: parsing a date in multiple formats
const date = /^(\d{4})-(\d{2})-(\d{2})|^(\d{2})\/(\d{2})\/(\d{4})$/;
// Now you have to check which group set matched. Cleaner — split into two:
const iso  = /^(\d{4})-(\d{2})-(\d{2})$/;
const us   = /^(\d{2})\/(\d{2})\/(\d{4})$/;
function parse(s) {
    let m;
    if ((m = iso.exec(s)))  return { y: +m[1], mo: +m[2], d: +m[3] };
    if ((m = us.exec(s)))   return { y: +m[3], mo: +m[1], d: +m[2] };
    return null;
}

// 14) Common bugs
//   • /^a|b$/ instead of /^(a|b)$/ — wrong anchor scope
//   • Java|JavaScript order — short alternative wins
//   • [a|b|c] — character class containing the literal | char
//   • Long lists like /one|two|three|…/  — switch to allowlist Set lookup

Why it matters

Always wrap alternation in a group when anchors or quantifiers are involved — ^cat|dog$ almost never means what people expect. And put the longer alternative first when one is a prefix of another, or the shorter one matches and the longer never gets tried.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
/cat|dog|bird/      // any of the three
/(cat|dog)s?/       // optional plural
Try it Yourself »

Discussion

Loading…