Bob van Luijt
Bob van Luijt

Reputation: 7588

JS RegExp() for finding occurrences

I'm trying to understand why this is returning false, and what I can do to make it true.

var str = "The best things in life are free";
var patt = new RegExp(/(?=best)(?=life)/i);
var res = patt.test(str); // = false. But expect true because both 'best' and 'life' are in the string

Upvotes: 0

Views: 42

Answers (1)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626738

The problem is that your lookaheads require best and life be at the same position in the string. Look-aheads are zero-width assertions and do not advance the RegExp index. Thus, your /(?=best)(?=life)/i checks at each position (if you move the lastIndex manually in the code) if the location is followed with best and life.

Use

/^(?=.*best)(?=.*life)/i

This regex will require best and life to be present at any location inside the string.

var str = "The best things in life are free";
var patt = /^(?=.*best)(?=.*life)/i;
var res = patt.test(str);
document.body.innerHTML = res;

Why /^(?=.*best)(?=.*life)/i but not /(?=.*best)(?=.*life)/i?

When not anchored, lookaheads are executed at each position. When we set the ^ in the beginning, the two lookaheads are executed one after another (if the previous one succeeded). Performance is much better when you only execute them once. In this case,however, there is no difference because there is no /g modifier in the regular expression.

Why not /best.*life|life.*best/?

You can, it will require an alternation, and in case you have a lot of such alternatives, it will be more difficult to maintain.

Why not use .indexOf?

Because the next possible enhancement is matching the two words as whole words:

/^(?=.*\bbest\b)(?=.*\blife\b)/i

So as not to match bestie or afterlife.

And the last, but not least note: if your string can contain a newline, replace the .* with [\s\S]* since a dot in a JS regex cannot match a newline.

Upvotes: 1

Related Questions