ES2018 - RegEx lookAhead and lookBehind
As I mentioned in the previous article, ES2018 has a few important additions related to regular expression—two of these which are quite nice are lookBehind and lookAhead.
lookAhead basically takes a regular expression from what comes after it. OK, what does that mean? Assume for example’s sake that I want a string to be passed so long as it contains the word ‘foo’ followed immediately by the word ‘bar’, but I don’t want to get the ‘bar.’ We can do this with the following expression:
const REGEX = /foo(?=bar)/
Parentheses with a question mark followed by an equals sign means “the thing that comes after what came before.” In our case foo comes before, then bar comes after.
const REGEX = /foo(?=bar)/ const a = REGEX.test('foobar'); // true const b = REGEX.test('fooba'); // false
One more example? Sure, why not! Here you go. Let’s say I get a telephone number and I only want to capture the area code. Remember, lookAhead doesn’t capture whatever comes after. This allows me to easily use the expression that says get everything after the hyphen. How does this look? First, the RegEx goes like this:
This means every single digit and more.
And this means that there must be it must be followed by a hyphen. Notice the hyphen comes after a slash.
So this is how it look in the wild:
const REGEX = /[0-9]+(?=\-)/ const a = ('054-45757575').match(REGEX); // 054
This captured only the 054. I could have done this another way, but in order to get only the area code I would need to use groups, while here I can use one RegEx to capture something.
This is a positive lookBehind, meaning something must come after the expression. If I want, I can use a negative lookBehind, which is when the expression won’t work if what’s ever in the lookBehind appears. This is also done with ease—just use an exclamation point instead of an equals sign. That’s it! Here, check out the foobar example but flipped around a bit. Now, the RegEx won’t work if there’s a bar after the foo.
const REGEX = /foo(?!bar)/ const a = REGEX.test('foobar'); // false const b = REGEX.test('fooba'); // true
If you want another example, here’s one where I capture all the first names wherever the last name is not Cohen.
const REGEX = /\S+ (?!Cohen)/ const a = 'Haim Levi'.match(REGEX); //Haim const b = 'Moshe Cohen'.match(REGEX); //Null
Here’s a codepen if you feel like messing around with it.
Just like how we have lookAhead that checks what comes after our expression (but captures only the expression), we also have lookBehind, which checks what comes before our expression (but captures only the expression). Example you say? I get a text string of a phone number and I don’t want the area code—just the rest of the number. How can I do this? Quite easily really. I’ll start by assuming that I want everything in front of a number and hyphen (like 03 or 054). This is also easy:
const REGEX_POSTIVE = /(?< =\-)\d+/ const c = '03-6382020'.match(REGEX_POSTIVE); //6382020 const d = '054-5555555'.match(REGEX_POSTIVE); //5555555
It’s just like lookAhead but there is also a less than before the =-. That’s it! There is of course negative lookBehind as well. For example, here I’m checking if there’s a number that doesn’t have a dollar sign before it.
const REGEX_NEGATIVE = /(?< !\$)\d/ const e = '$4'.match(REGEX_NEGATIVE); //false const f = '₪4'.match(REGEX_NEGATIVE); // 4
How does this work? Easy. The expression !>? Is the negative lookBehind. Immediately after I put the expression that I don’t want to go in. In this case a dollar sign. Don’t forget the \- after since this is a special character to capture the parentheses. After this I just write \d which means a number. That’s it.
I know RegEx can be a bit annoying sometimes, but lookAhead and lookBehind are really useful and it’s great that they finally made it into the new standard.
About the author: Ran Bar-Zik is an experienced web developer whose personal blog, Internet Israel, features articles and guides on Node.js, MongoDB, Git, SASS, jQuery, HTML 5, MySQL, and more. Translation of the original article by Aaron Raizen