Skip to Content
Course content

152: Lookahead and Lookbehind in JS Regex

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I see this a lot when developers start parsing strings for data: they write a regex that finds exactly what they're looking for, but the regex "grabs" too much. They end up with a match that includes the anchor or label they used to find the value, and then they have to write extra code to trim or slice the result.

The "NaN" Currency Trap

const priceList = "Total: $120, Tax: $10, Shipping: $5";
const matches = priceList.match(/\$\d+/g); 

// I want an array of numbers to sum them up
const totals = matches.map(price => parseInt(price));

console.log(totals); // [NaN, NaN, NaN]

If you've run into this, you know the frustration. The regex /\$\d+/g is doing exactly what we told it to: find a dollar sign followed by digits. The problem is that parseInt doesn't know how to handle that $ character. It sees the symbol at the start, gives up immediately, and returns NaN.

Usually, the "quick fix" is to use a capture group or call .slice(1) on every match. But that's messy. We're adding extra steps to clean up a match that should have been clean to begin with. We want the digits, but only if they are preceded by a dollar sign. We don't actually want the dollar sign in our result.

Using Positive Lookbehind for Clean Extraction

This is where Lookbehind comes in. A lookbehind tells the regex engine: "Check if this pattern exists behind the current position, but don't actually include it in the match."

The syntax is (?<=pattern). Let's rewrite that price extractor:

const priceList = "Total: $120, Tax: $10, Shipping: $5";
// This says: Match digits, but only if they follow a '$'
const matches = priceList.match(/(?<=\$)\d+/g); 

const totals = matches.map(price => parseInt(price));
console.log(totals); // [120, 10, 5]

Notice the difference? The $ is still there in the string—the regex engine uses it as a landmark—but it isn't "consumed." The match only contains the digits. This makes the rest of your data pipeline much smoother because you aren't fighting with your own regex results.

Filtering Context with Positive Lookahead

Lookahead is the mirror image of lookbehind. It checks if a pattern follows the current position without including that pattern in the match. The syntax is (?=pattern).

I used this recently when parsing a configuration file where I needed to find IDs, but only if they were specifically marked as (active). If I used a standard match, I'd get the (active) string attached to every ID.

const config = "user_123(active), user_456(inactive), user_789(active)";

// Match digits only if they are followed by '(active)'
const activeUsers = config.match(/\d+(?=\(active\))/g);

console.log(activeUsers); // ["123", "789"]

If you're wondering why you wouldn't just use a capture group, remember that .match() with the /g flag ignores capture groups and returns the full match. Lookaheads allow you to filter your matches based on the surrounding context without polluting your result array.

Handling the "Not" Case (Negative Lookarounds)

Sometimes you need the opposite: you want to match something only if a certain pattern isn't there. This is called a Negative Lookaround.

  • Negative Lookbehind: (?<!pattern) — Match if the pattern does NOT precede.
  • Negative Lookahead: (?!pattern) — Match if the pattern does NOT follow.

Imagine you want to find all numbers in a string, but you want to ignore any number that is specifically a price (preceded by $). You'd use a negative lookbehind:

const text = "The item costs $50, but the quantity is 2";
// Match digits, provided they are NOT preceded by '$'
const nonPrices = text.match(/(?

I'll leave you with a word of caution: lookarounds can be expensive for the regex engine if you're running them against massive blocks of text. They aren't "slow" in the traditional sense, but they add complexity to how the engine scans the string. Use them when they make your code more readable, but keep an eye on performance if you're processing megabytes of logs.




📋 Practical Task

Exercise: Parsing a System Log for Non-Error Codes

You are building a log analyzer. You have a string of system events. Each event has a code (a 3-digit number). Some codes are followed by the tag [CRITICAL], and some are followed by [INFO].

Your task is to extract all the 3-digit codes that are NOT critical. Use a Negative Lookahead to ensure that any number followed by [CRITICAL] is ignored, while numbers followed by [INFO] (or nothing at all) are captured.

const logData = "101[INFO], 404[CRITICAL], 200[INFO], 500[CRITICAL], 302[INFO], 999";

// Your code here:
const regex = // ... write your regex here
const safeCodes = logData.match(regex);

console.log(safeCodes); 
// Expected Output: ["101", "200", "302", "999"]
Rating
0 0

There are no comments for now.

to be the first to leave a comment.