/^/

Lesson 11 of 28

Alternation

Use | to match one of several alternatives, and learn the operator-precedence gotchas that make ^cat|dog$ behave very differently from ^(?:cat|dog)$.

7 min read

The pipe | means “or” — it lets a single pattern match one of several alternatives. It’s one of the simplest pieces of regex syntax, and also one of the easiest to get subtly wrong, because | has lower precedence than almost everything else in a pattern.

The basics

Alternation tries each option, separated by |, until one matches:

/cat|dog|bird/.test("I have a dog"); // true
/cat|dog|bird/.test("I have a fish"); // false

You can combine alternation with grouping to apply it to just part of a pattern — for example, matching either “gray” or “grey”:

/gr(?:a|e)y/.test("gray"); // true
/gr(?:a|e)y/.test("grey"); // true

The precedence gotcha

Here’s where it gets tricky. | doesn’t just apply to the option immediately next to it — it splits the entire pattern at that point, unless you scope it with a group. This means ^cat|dog$ is not “match a line that’s cat or dog” — it’s really (^cat)|(dog$): match “cat” at the start of a line, or, completely independently, match “dog” at the end of a line.

cat dog cat nap a big dog catalog dogma
5 matches

Look closely at the matches: "catalog" matches too, even though it’s clearly not the word “cat” on its own — because ^cat only requires “cat” at the start of the line, with nothing said about what comes after. Meanwhile "dogma" matches nothing, because dog$ requires “dog” at the very end of the line, and “dogma” ends in “ma”.

Now compare that to the same alternatives, properly scoped inside a non-capturing group so ^ and $ apply to the whole thing:

cat dog cat nap a big dog catalog dogma
2 matches

This version only matches lines that are exactly “cat” or “dog” — “catalog” and “cat nap” are correctly excluded now, because the anchors wrap the entire alternation instead of just one branch of it.

// Looks like it means "start or end with cat/dog" — it doesn't
/^cat|dog$/

// Actually means "the whole line is exactly cat or dog"
/^(?:cat|dog)$/

Watch out

Whenever you mix | with ^, $, or any assertion, ask yourself: does this anchor apply to just the neighboring alternative, or to the whole group? If in doubt, add explicit parentheses — it costs nothing and removes all ambiguity.

Ordering matters when one alternative is a prefix of another

Alternation tries options left to right and stops at the first one that lets the rest of the pattern succeed — it does not search for the longest possible match. If a shorter alternative is a prefix of a longer one and is listed first, the longer one may never get a chance:

catalog cat
2 matches

Even though the full word “catalog” is right there in the text, the pattern only ever matches "cat" — the first alternative succeeds immediately, so the engine never tries "catalog". Flip the order and the longer alternative wins when it can:

catalog cat
2 matches
Pattern Against “catalog” Why
cat|catalog Matches "cat" only Shorter alternative tried first, succeeds, engine stops
catalog|cat Matches "catalog" Longer alternative tried first, succeeds, captures the whole word

Fix the precedence bug

You want a pattern that matches a line that is exactly "yes" or exactly "no" — nothing else. A teammate wrote ^yes|no$. Explain why it’s wrong, and fix it.

Show solution

^yes|no$ is really (^yes)|(no$) — it matches “yes” at the start of a line (even as part of a longer word like “yesterday”) or “no” at the end of a line (even as part of “casino”), independently of each other.

The fix is to scope the alternation with a group so both anchors apply to the whole thing:

/^(?:yes|no)$/

Now the pattern only matches a line that is exactly “yes” or exactly “no”, nothing more and nothing less.

What’s next

Alternation matches one of several fixed options. Next, you’ll learn lookahead — a way to check what comes next in the string without actually matching (or consuming) it.

Quick check

What does the pattern ^cat|dog$ actually mean?

Matching the pattern /cat|catalog/ against the string "catalog", what does match[0] equal?