This is ridiculous. Such error messages should not exist

Let's assume I need to catch a text which contains one and only one letter "a".

let text = "abcdefg";
if(text.match(/a/g).length == 1) console.log(`success`) // true
text = "aabcdefg"
if(text.match(/a/g).length == 1) console.log(`success`) // false

Everything is fine.

But let's say a text does not have the letter "a" at all:

text = "hijklmnop"
if(text.match(/a/g).length == 1) console.log(`success`)

I get the error message "Cannot read properties of null (reading 'length')", and my program can't continue. I need to come up with a "solution" like:

text = "hijklmnop"
if(text.match(/a/g) && text.match(/a/g).length == 1) console.log(`success`)

which:

a) makes the code more ugly
b) makes me fight the compiler instead of, you know, programming

My solution would be that if a ".match" function does not find anything it returns an empty array (which has a length of 0) instead of null. Or it could handle "if (text.match(/a/g).length==3)" in a special way that would not give you a compiler error in case of zero matches.

Finding a match and evaluating a matched result set are two different things. So I wouldn't rely on the array result of match() for your case.

If you want to just assert that any match is present and don't care about how many matches, I'd use match(). But if you also want to know how many matches are present or want to guarantee an array result set, I'd use

const text = "abcdefg";
const array = [...text.matchAll(/a/g)]

Then you'll always have an array result.

2 Likes

There's also the ?. operator.

if(text.match(/a/g)?.length == 1) console.log(`success`)

Recall that in some cases (when the g flag isn't used), .match() will not return just a normal array, it will return an array populated with various properties the same way .exec() does (see here). It's impossible to populate those special properties on the array if we failed to match anything, so in this kind of scenario, it only makes sense to return null when nothing matches. So, in some ways, one could argue that the function is just being consistent when it returns null when nothing matches when you use the global flag.

2 Likes

Yeah I think OP's issue just boils down to the fact that they want to use .length. But given this example, I don't even see why an array result would matter if they just want to evaluate whether there is any match. Could easily just ditch the length and make it

if(text.match(/a/g)) console.log(`success`) // true

But there may be some additional context OP left out that we're not aware of.

From what it sounded like, they're wanting to make sure there's exactly one match, not one or more. (They explicitly have an example string with multiple "a"s that fail their check). So simply checking if the result is null or not wouldn't give them the behavior they need.

so, /^[^a]*a[^a]*$/g.test(str)?

2 Likes

so, /^[^a]*a[^a]*$/g.test(str)

What I was gonna say. And it's faster than /a/g too.

Show off :wink:

use ?.

text.match(/a/g)?.length

@theScottyJam covered that