I was working on a small Android project using JavaScript with DroidScript, aiming to build a helper tool for the game Word Snack. The idea was simple: check if two words contain the exact same letters, just like in anagram logic.
I wanted to compare two strings and verify that they had the same letter frequencies not just the same letters. So I thought: I’ll just loop through the characters and use .match()
to count how many times each letter appears. Boom, done. Right.
Error Code
While testing my code, I was hit with a frustrating error:
Cannot read property 'length' of null
It pointed to this line in my function:
if((from_file.match(new RegExp(from_file[i], "g") || [])).length != (searched.match(new RegExp(from_file[i], "g") || [])).length)
At first glance, it looked totally fine. I even used the || []
fallback to avoid errors. But no JavaScript had other plans.
What the Real Problem with .match()
Here’s the thing:.match()
returns null
when no matches are found. It does not return an empty array.
That means if I try to access .length
of that result, and no match occurred, I’m literally asking for null.length
, and boom, runtime error.
Now let’s break down my original misunderstanding:
from_file.match(new RegExp(from_file[i], "g") || [])
I thought I was being safe with || []
. But that fallback applies to the RegExp
constructor, not to the result of .match()
. So if .match()
returned null
, the error still happened.
What the Right Way to Handle
The correct way is to make sure the || []
fallback is applied to the result of .match()
, like this:
(from_file.match(new RegExp(from_file[i], "g")) || []).length
Wrapping .match()
in parentheses ensures the fallback is applied to the result, not the regex itself.
My Update Code with Extra Functionality
After fixing the bug, I decided to make the script more insightful. I added a helper function to show letter frequencies that way I could visualize why two words weren’t considered a match.
Here the improved version of the project:
function OnStart() {
var lay = app.CreateLayout("linear", "VCenter,FillXY");
// Example strings (can later be loaded from file)
var from_file = "dedo";
var searched = "odod";
// Compare the words
if (compare_words(from_file, searched)) {
var txt = app.CreateText(from_file + " == " + searched + ": MATCH");
} else {
var txt = app.CreateText(from_file + " != " + searched + ": NO MATCH");
}
txt.SetTextSize(32);
lay.AddChild(txt);
// Show letter frequency comparison
var freqText = app.CreateText("Letter frequencies:\n" +
getFrequencies(from_file, "from_file") + "\n" +
getFrequencies(searched, "searched"));
freqText.SetTextSize(22);
lay.AddChild(freqText);
app.AddLayout(lay);
}
// Compare two strings by character frequency
function compare_words(from_file, searched) {
if (from_file.length !== searched.length) return false;
for (var i = 0; i < from_file.length; i++) {
var letter = from_file[i];
var count1 = (from_file.match(new RegExp(letter, "g")) || []).length;
var count2 = (searched.match(new RegExp(letter, "g")) || []).length;
if (count1 !== count2) return false;
}
return true;
}
// Build frequency map for display
function getFrequencies(str, label) {
var freq = {};
for (var i = 0; i < str.length; i++) {
var ch = str[i];
freq[ch] = (freq[ch] || 0) + 1;
}
var output = label + ":\n";
for (var key in freq) {
output += key + ": " + freq[key] + "\n";
}
return output;
}
Sample Output
Let’s say we compare these:
from_file = "dedo";
searched = "odod";
You’ll get:
dedo != odod: NO MATCH
Letter frequencies:
from_file:
d: 2
e: 1
o: 1
searched:
o: 2
d: 2
Even though the letters seem similar, "e"
is missing from searched
, so it’s not a match. The visual breakdown helps spot these mismatches easily.
Developer Tips I Learn
Here’s what I took away from this little adventure:
- Always guard
.match()
with|| []
when counting matches. - Use character frequency maps when comparing strings they’re faster and clearer than looping
.match()
repeatedly. - Pay close attention to where you put your fallbacks (
|| []
). It must go after.match()
, not insideRegExp()
. - If you don’t care about frequencies, you can sort both strings and compare them for simple anagram checks.
- In Android projects with JavaScript (e.g. DroidScript), even small errors like
null.length
can break the entire UI logic.
Final Thought
What started as a simple string comparison turned into a great debugging lesson. I not only solved my error but also improved the tool and added new features to make it more user-friendly.
That one tiny change moving || []
outside the .match()
literally saved me hours of hair pulling.
If you’re building tools like this using JavaScript (especially in constrained environments like mobile), be extra careful with return values like null
. JavaScript can be sneaky just because something looks like an array doesn’t mean it is.