BlogCode

How to create and access Regex groups in JavaScript

Written by Codemzy on January 12th, 2022

Take your regex further by breaking it down and accessing specific parts of your match with groups. Find out how you can create and use groups inside your regex with match() and matchAll().

Regular expressions are great for checking strings for patterns. Maybe a word, a number, or something more complex.

You can use regex to do all sorts of things. Check if a pattern exists, replace sections of a string, count the number of times a specific word or pattern occurs.

But you might also want to match a pattern in regex and only use a small part of it. For example, you might have a string of HTML and need to check the value of an input.

Creating regex groups

Groups let you match specific patterns inside your regex. Or pull out a part of a pattern.

Imagine somewhere inside a long string of HTML content, there is a name input.

<input type="text" name="username" value="the info you want">

Since you don’t know what the exact value of the input is, you can’t create a simple regex match for it. Luckily you can use regex tokens like .*? to match any value (.* matches any characters between zero and unlimited times, and ? makes it none greedy (lazy), so once it hits the next " it stops matching).

let result = theHTMLString.match(/<input type="text" name="username" value=".*?">/m);

This regex matches our input, great! But we only need that unknown value, not the entire match. And that is where groups are useful.

You create groups in regex by using parenthesis () around a part of your match pattern. And you’re not limited to one, you can have multiple groups in your match.

Let’s add a group to our regex match.

let result = theHTMLString.match(/<input type="text" name="username" value="(.*?)">/m);

This part of the match (.+?) that matches the value is now a group, so we can access it from our result array. The first array entry result[0] is the full match, but the second array entry result[1] is the first group match.

let result = theHTMLString.match(/<input type="text" name="username" value="(.*?)">/m);
let value = result[1]; // 'the info you want'

Nice!

You might also notice the result has a groups object (result.groups) that is undefined. That's because we didn't name the group yet, more on that in a bit!

Accessing groups with the global flag

You might also want to use groups for multiple matches, using the global g flag. For example, you might have an HTML string and need to find every checkbox inbox input, and know if it’s checked or not.

Here’s the HTML for this example.

<h1>Looking for checked inputs</h1>
<p>This is just a simple example you can check out, for using regex groups to looks for checked inputs.</p>
<div>
  <input type="checkbox" id="check1" name="first" checked>
  <label for="check1">First</label>
  <input type="checkbox" id="check2" name="second" checked>
  <label for="check2">Second</label>
  <input type="checkbox" id="check3" name="third">
  <label for="check3">Third</label>
</div>

Ok, so we have three checkboxes, and two are checked. No need for regex to work that one out! But please use your imagination on this one. Let's say we had hundreds of checkboxes scattered throughout the HTML. I wouldn't fancy going through that manually and counting them!

Using regex without groups, you could look for checkbox inputs.

let results = theHTMLString.match(/<input type="checkbox".*?>/gm);
console.log(results.length); // 3

But that tells you how many inputs there are, and not if they are checked or not.

To solve this, you could run two very specific matches, one that only matches checked inputs, and one that only matches unchecked inputs. Or you can use groups.

In this example, the regex can match all checkbox inputs, but we can use a group inside the pattern to see if it is checked or not.

So let’s add a group to our regex match.

let results = theHTMLString.match(/<input type="checkbox".*?(checked)>/gm);
console.log(results.length); // 2

This still isn’t perfect, it matches checked inputs but not unchecked ones because they don’t contain the group. So you need to make the group optional. To do this you can add a ? directly after the group.

let results = theHTMLString.match(/<input type="checkbox".*?(checked)?>/gm);
console.log(results.length); // 3

Super, the three inputs are matched, and we have our group. But we only know how many checkboxes there are, we don't yet know how many are checked. To do this, we need to access the group.

If you need to get group information from a match with the g flag, well, you can't. Not in the same way we did in the first example.

String.match() won't return groups if the /.../g flag is set. However, you can still use String.matchAll() to get all matches.

- MDN Regular expressions > Groups and ranges

So when using groups with a global match, we only get matches back - not the groups. If we need the group information, we can use matchAll instead.

let results = theHTMLString.matchAll(/<input type="checkbox".*?(checked)?>/gm);
let checkboxes = 0;
let checked = 0;
for (let result of results) {
  checkboxes++;
  if (result[1]) {
   checked++;
  }
}
console.log(checkboxes); // 3
console.log(checked); // 2

Using result[1] gives us the first group, which is undefined if there is no match for the group. So the above code loops over the results, and if match[1] exists, adds that to the checked tally.

It's worth noting here that the result from matchAll can be looped, but it's not an array. If you want to use an array method like .map() you need to convert your results object into an array first.

let results = theHTMLString.matchAll(/<input type="checkbox".*?(checked)?>/gm);
results = Array.from(results); // turn results into an array
let checkboxes = results.length;
let checked = 0;
results.map(function(result) {
  if (result[1]) {
   checked++;
  }
});
console.log(checkboxes); // 3
console.log(checked); // 2

Naming regex groups

So far to access our group we have remembered its place in the result array - which is fine when you only have a couple of groups.

But if you have more than a few groups, it can get messy.

Luckily, you can also name your groups, so that they are accessible on the groups object mentioned earlier.

To name a group, you give it a name when you create it. Instead of (some-pattern), you can add a name with ?<groupName> at the start of the group, like (?<firstName>some-pattern).

Going back to our input value match in the first example, let’s name the group.

let result = theHTMLString.match(/<input type="text" name="username" value="(?<value>.*?)">/m);
let value = result.groups.value; // 'the info you want'

And when using matchAll, each result within the results object will contain its groups information.

let results = theHTMLString.matchAll(/<input type="checkbox".*?(?<checked>checked)?>/gm);
let checkboxes = 0;
let checked = 0;
for (let result of results) {
  checkboxes++;
  if (result.groups.checked) {
   checked++;
  }
}
console.log(checkboxes); // 3
console.log(checked); // 2

Naming groups might seem like some extra work in this example, and it is. But when you use lots of groups, it can help improve the readability of your code.


One of the best uses for groups is when using regex to make changes. The next blog post will look at some examples of using regex groups with .replace().