Reputation: 7588
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
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