Published at
Updated at
Reading time
2min
This post is part of my Today I learned series in which I share all my web development learnings.

Today I was preparing a slide deck about new features in JavaScript regular expressions and came across the article "Named capture groups" written by Axel Rauschmayer. The section about backreferences caught my eye.

In some situations you might want to create a regular expression that includes repeated character sequences like the following one: /(abc)(abc)(abc)/. Instead of copying the character groups several times, you want to reuse the pattern. Is this possible in regular expressions? You bet!

Backreferences for capture groups

When you define your regular expressions, you can reuse and backreference previous groups via \1, \2, etc..

/(๐Ÿ•)(๐ŸŒฏ)\1\2/.exec('๐Ÿ•๐ŸŒฏ๐Ÿ•๐ŸŒฏ');
// (3) ["๐Ÿ•๐ŸŒฏ๐Ÿ•๐ŸŒฏ", "๐Ÿ•", "๐ŸŒฏ", index: 0, input: "๐Ÿ•๐ŸŒฏ๐Ÿ•๐ŸŒฏ", ... ]
// Match: 
// - a pizza
// - a burrito
// - a pizza (backreferenced)
// - a burrito (backreferenced)

/(๐Ÿ•)(๐ŸŒฏ)\1\2/.exec('๐Ÿ•๐ŸŒฏ๐Ÿ•');
// null (because one burrito is missing)

Backreferences for named capture groups

You can do the same for named capture groups via \k<name>.

/(?<one>๐Ÿ•)(?<two>๐ŸŒฏ)\k<one>\k<two>/.exec('๐Ÿ•๐ŸŒฏ๐Ÿ•๐ŸŒฏ');
// (3) ["๐Ÿ•๐ŸŒฏ๐Ÿ•๐ŸŒฏ", "๐Ÿ•", "๐ŸŒฏ", index: 0, input: "๐Ÿ•๐ŸŒฏ๐Ÿ•๐ŸŒฏ", groups: {โ€ฆ}]
// Match:
// - a pizza
// - a burrito
// - a pizza (backreferenced via the named capture group 'one')
// - a burrito (backreferenced via the named capture group 'two')

/(?<one>๐Ÿ•)(?<two>๐ŸŒฏ)\k<one>\k<two>/.exec('๐Ÿ•๐ŸŒฏ๐Ÿ•');
// null (because one burrito is missing)

References in string replacements

Arnd Issler pointed out, that you can not talk about back references in regular expression without mentioning the references when using String.prototype.replace().

So, here we go. ๐Ÿ˜Š

Replacement references for capture groups

Turns out, if you use replace with a regular expression, you can reference included capture groups using $1, $2, etc..

MDN provides a good example to swap words using references.

const re = /(\w+)\s(\w+)/;
const str = 'Jane Smith';
const newstr = str.replace(re, '$2, $1');
console.log(newstr);  // Smith, Jane

To follow the earlier examples you can have a look at the following "pizza-burrito-snippet":

'๐Ÿ•๐ŸŒฏ๐Ÿ•'.replace(
  /(๐Ÿ•)(๐ŸŒฏ)\1/,
  'first group: $1, second group: $2'
);
// "first group: ๐Ÿ•, second group: ๐ŸŒฏ"

Note that when using $1 and $2 in the replace function these match with the first and second regular expression capture group. $1 references the ๐Ÿ•, and $2 references the ๐ŸŒฏ. Wild stuff!

But as sequences such as $1 and $2 reference capture groups you might wonder how you replace something with $1 without referencing an included capture group. In that case, you can use e.g. $$1.

'๐Ÿ•๐ŸŒฏ๐Ÿ•'.replace(
  /(๐Ÿ•)(๐ŸŒฏ)\1/,
  '$$1 $$2'
);
// "$1 $2"

Replacement references for named capture groups

The same reference functionality works for named capture groups using $<name>:

'๐Ÿ•๐ŸŒฏ๐Ÿ•'.replace(
  /(?<one>๐Ÿ•)(?<two>๐ŸŒฏ)\k<one>/,
  'first group: $<one>, second group: $<two>'
);
// "first group: ๐Ÿ•, second group: ๐ŸŒฏ"

And similarly, if you want to replace something with $<name> if there is a named capture group present you can use $$<name>;

'๐Ÿ•๐ŸŒฏ๐Ÿ•๐ŸŒฏ๐Ÿ•๐ŸŒฏ'.replace(
  /(?<one>๐Ÿ•)(?<two>๐ŸŒฏ)\k<one>/,
  '$$<one> $$<one> $$<one> โ€“ '
);
// "$<one> $<one> $<one> โ€“ ๐ŸŒฏ๐Ÿ•๐ŸŒฏ"

I love these tiny regex details! ๐Ÿ˜… If you do, too, you should definitely look at other replacement patterns of String.prototype.replace that include more magic than you think.

Enjoy, and see you next time. ๐Ÿ‘‹

Was this TIL post helpful?
Yes? Cool! You might want to check out Web Weekly for more quick learnings. The last edition went out 12 days ago.
Stefan standing in the park in front of a green background

About Stefan Judis

Frontend nerd with over ten years of experience, freelance dev, "Today I Learned" blogger, conference speaker, and Open Source maintainer.

Related Topics

Related Articles