Skip to content
the words 'string' in retro style and 'methods' in neon style over a synthwave background

JS String Methods - Part 8

match() and matchAll()

Tags that this post has been filed under. Links open in the same window.

match() and matchAll(), a RegExp lover's dream (and also a cool playground)

In this post, skip to:

match

match() returns the result, in Array form, of matching a string against the regular expression you pass as a parameter.

If you pass a string or number, it'll be converted into a regular expression under the hood, with new RegExp(). Ideally you'll use a regular expression directly so you'll be able to better cover edge cases, though.

If you want to retrieve all matches and not just the first one, you can optionally use the RegExp global flag g.

Here are some examples:

let string = "We're going on an adventure, Charlie!"

string.match(/C/)
//output: ['C', index: 29, input: "We're going on an adventure, Charlie", groups: undefined]

string.match('C')
//output: ['C', index: 29, input: "We're going on an adventure, Charlie", groups: undefined]

string.match(/[A-Z]/g)
//output: (2) ['W', 'C']

string.match(/[A-Z]/)
//output: ['W', index: 0, input: "We're going on an adventure, Charlie", groups: undefined]

string.match("!")
//output: null

string.match("e")
//output: ['e', index: 1, input: "We're going on an adventure, Charlie", groups: undefined]

string.match(/e/g)
//output: (5) ['e', 'e', 'e', 'e', 'e']

You can read more about RegExp.prototype[@@match] in the MDN docs, they have demos and examples too!

matchAll

It gets a little more complex here. It's not just an implementation of match() with a global flag built-in - in fact, if you use a regular expression as a parameter and forget to include /g then you'll get a TypeError. You may choose to pass something else as the parameter, but it will be implicitly converted to RegExp anyway.

This actually returns an iterator, in object form, so we can play around with it using for...of, Array.from() or array spreading.

Let's take a look and explore those returns:

let string = "We're going on an adventure, Charlie!"

// using an expression to find lowercase vowels followed by "n" then a space
let array1 = Array.from(string.matchAll(/([aeiou]n\s)/g))

console.log(array1)
//output: (2) [Array(2), Array(2)]

array1[0]
//output: ['on ', 'on ', index: 12, input: "We're going on an adventure, Charlie", groups: undefined]

array1[1]
//output: ['an ', 'an ', index: 15, input: "We're going on an adventure, Charlie", groups: undefined]

//------------
// now the expression finds any uppercase letter in the alphabet followed by a lowercase letter
let array2 = [...string.matchAll(/([A-Z][aeiou])/g)]
//output: [Array(2)]

array2[0]
//output: (2) ['We', 'We', index: 0, input: "We're going on an adventure, Charlie", groups: undefined]

//------------
//let's find lowercase vowels followed by "n" and loop through them
let array3 = string.matchAll(/([aeiou]n)/g))

for (entry of array3){
console.log(`"${entry[0]}" starting at string[${entry.index}]`)
}
//output:
/*
"in" starting at string[8]
"on" starting at string[12]
"an" starting at string[15]
"en" starting at string[21]
*/

matchAll capturing groups

matchAll() also allows capturing groups, which allows us to further classify and reuse the matches we've had.

Let's try something different this time.
We're going to find all uppercase characters optionally followed by something other than a vowel.
Then, we'll name two groups: uppercase and notVowel.
Finally, we'll loop through them, saying *crickets* if notVowel is undefined (meaning there was nothing found in that part of the search)

let string = "We're going on an adventure, Charlie!"

let dizzying = [...string.matchAll(/(?<uppercase>[A-Z])(?<notVowel>[^aeiou])*/g)]

console.log(dizzying)
//output: (2) [Array(3), Array(3)]

console.log(dizzying[0], dizzying[1])
//output:
// (3) ['W', 'W', undefined, index: 0, input: "We're going on an adventure, Charlie", groups: {…}]
// (3) ['Ch', 'C', 'h', index: 29, input: "We're going on an adventure, Charlie", groups: {…}]

// Oh look! Now groups are not "undefined"! Let's take a look inside:

console.log(dizzying[0].groups, dizzying[1].groups)
//output:
// {uppercase: 'W', notVowel: undefined}
// {uppercase: 'C', notVowel: 'h'}

for (entry of dizzying){
console.log(`Found ${entry.groups.uppercase} at ${entry.index}, followed by... ${entry.groups.notVowel === undefined ? '*crickets*' : entry.groups.notVowel}!`)
}

//output:
/*
Found W at 0, followed by... *crickets*!
Found C at 29, followed by... h!
*/

So yeah, it can be a little overwhelming but matchAll() gives us tool to play around!

Time for practice!

Try this easy CodeWars kata if you've got the time:

There are excellent katas to practice at jskatas.org, too.