Lookaheads in Javascript
Tags: todayilearned, javascript, beginners
Published: 2019-08-01 10:44:28

Today I faced a very interesting exercise. I had to make a function that took a string as its argument and returned the same string but written in spinal tap case (which apparently is the same as or very similar to [kebab case](https://en.m.wiktionary.org/wiki/kebab_case), just-like-this). Up until here, there's nothing to fear, I knew I could just `.split().join().toLowerCase()` it away. The challenge for me came when I had to turn a string such as `"ThisIsAString"` into spinal tap. My first thought was *how on earth do I divide a string by its capitals __and keep the capitals at the same time__?*

The answer to my problem is lookaheads. I had learned about them a while ago, but hadn't really understood them. So this exercise was the perfect opportunity to actually learn how to use them.

The first bit of the exercise was quite simple. So I'm just going to put it here and not say much about it. This creates an array separating where there is a space or an underscore. Then joins it againt into a string separating each element with a hyphen and then turns it all to lower case. Easy peasy.
```javascript
const toSpinal = str => str.split(/\s|_/).join('-').toLowerCase()
```

As you'll have noticed, when you turn a string into an array using `split()`, it also removes the character(s) that mark the boundaries between elements. So in our example, the string `"I Like_Potatoes"` would become `"i-like-potatoes"`.

###So, What about These Lookaheads?

After this lengthy introduction, let's return to the problem at hand. My function so far works very well for anything that isn't `"ILikePotatoes"`. But to adecuately process such string, I know that I can't simply use a regular expression like `[A-Z]`, because that would also remove the capital letters. So I'd get something like `"--ike-otatoes"`.

This is where lookaheads come in handy. Lookaheads allow us to check whether an expression A is followed by another expression B. It will only return A if B follows (or doesn't follow) it, but it will leave B unmatched and untouched. We write a lookahead using `(?=)` (or `(?!)` if we want the expression **not** to be followed by the lookahead).

```javascript
const regEx = /[A-Z](?=\W)/g
const str = "A. Potato, B- Onion, C: All of the above"
str.match(regEx) // returns A, B, C
```

The lookahead `(?=\W)` tells the program to look for any non-word character after the previous expression. So all capital letters that are **not** followed by a non-word character, will not return a match. But notice that the `match()` only returns the three capital letters A, B and C, not the character that follows them.

If we wanted the string to return a match for capital letters that are **not** followed by a non-word character, we could simply use a negative lookahead (`(?!)`):

```javascript
const regEx = /[A-Z](?!\W)/g
const str = "A. Potato, B- Onion, C: All of the above"
str.match(regEx) // returns P, O, A
```

If we write a regular expression with a lookahead but **with no preceeding expression**, it will match all that matches the lookahead but return empty. We can use this to our advantage when we want to turn the string `"ILikePotatoes"` into spinal tap case:

```javascript
const toSpinal = str => str.split(/\s|_|(?=[A-Z])/).join('-').toLowerCase() //Note that the lookahead comes after a | so it isn't attached to the previous expression

console.log(toSpinal("I like potatoes")) //i-like-potatoes
console.log(toSpinal("ILikeCarrots")) //i-like-carrots
console.log(toSpinal("ThisIsA_really We1rdString")) //this-is-a-really-we1rd-string
```

###Conclusion

Lookaheads allow us to define whether an expression will be matched or not based on what follows it (or doesn't). So it will only match an expression if both the expression itself and the lookahead match. But it will only return the expression, without the lookahead.