One of the funniest things about programming is finding all the available ways to solve a problem. Today I want to find 10 methods to check if a string contains a substring in JavaScript.
String.prototype.includes()
Let’s start with the best solution, the String.prototype.includes()
method. This method allows you to check if a string contains a substring, and returns a boolean value.
const string = "Hello World!";
console.log(string.includes("Hello")); // true
console.log(string.includes("!")); // true
console.log(string.includes("Hello World!")); // true
console.log(string.includes("Hello World!!")); // false
Its main limitation is that it is case-sensitive, so it doesn’t work with strings that contain uppercase and lowercase characters.
const string = "Hello World!";
console.log(string.includes("hello")); // false
We can fix this by making the string lowercase before performing the check.
const string = "Hello World!";
const substring = "HeLLo";
console.log(string.toLowerCase().includes(substring.toLowerCase())); // true
String.prototype.indexOf()
A second method uses String.prototype.indexOf()
. This method returns the index of the first occurrence of a substring within a string, or -1 if not found. I can then convert the index to a boolean value.
const string = "Hello World!";
console.log(string.indexOf("Hello") !== -1); // true
console.log(!string.indexOf("Hello")); // true
Again, the method is case-sensitive.
const string = "Hello World!";
const substring = "HeLLo";
console.log(!string.indexOf(substring)); // false
console.log(!string.toLowerCase().indexOf(substring.toLowerCase())); // true
Polyfill
If I have to use a legacy browser (I sometimes do), it is still possible to create a polyfill to add the String.prototype.includes()
method. To do this I use String.prototype.indexOf()
again but hide it in an includes()
method.
if (!String.prototype.includes) {
String.prototype.includes = function (search, start) {
"use strict";
if (typeof start !== "number") {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
Knuth–Morris–Pratt algorithm
Another solution is to use the Knuth–Morris–Pratt. This algorithm, from the 1970s, allows very fast searching, and is often used as a basis for other string search algorithms. This isNayuki’s version
function kmpSearch(pattern, text) {
if (pattern.length == 0) return 0; // Immediate match
// Compute longest suffix-prefix table
var lsp = [0]; // Base case
for (var i = 1; i < pattern.length; i++) {
var j = lsp[i - 1]; // Start by assuming we're extending the previous LSP
while (j > 0 && pattern[i] !== pattern[j]) j = lsp[j - 1];
if (pattern[i] === pattern[j]) j++;
lsp.push(j);
}
// Walk through text string
var j = 0; // Number of chars matched in pattern
for (var i = 0; i < text.length; i++) {
while (j > 0 && text[i] != pattern[j]) j = lsp[j - 1]; // Fall back in the pattern
if (text[i] == pattern[j]) {
j++; // Next char matched, increment position
if (j == pattern.length) return i - (j - 1);
}
}
return -1; // Not found
}
console.log(kmpSearch("ays", "haystack") != -1); // true
console.log(kmpSearch("asdf", "haystack") != -1); // false
Other solutions
But these are not the only paths we can take. There are other possibilities, as long as you push JavaScript a bit. I don’t recommend using them, but they are interesting to understand how JavaScript works.
In the next examples I will use two variables, string
and substring
, which will contain respectively the string and the substring to search for.
const string = "Hello World!";
const substring = "Hello";
I can use String.prototype.match()
to match the result of a regular expression search. If the search finds nothing, it returns null
. I then convert the result into a boolean value.
console.log(!!string.match(substring)); // true
Similarly String.prototype.search()
returns -1
if the search finds nothing. So I just check that the result is greater than or equal to zero.
console.log(string.search(substring) >= 0); // true
Another method can be to replace the substring with an empty value and check that the length of the string has changed. Or I can also simply verify that it is not equal to itself. To do this I use the String.prototype.replace()
method
console.log(string.replace(substring, "") != string); // true
console.log(string.replace(substring, "").length != string.length); // true
startWith() and endsWith()
Finally, if I want to check only the start or end I can use String.prototype.startsWith()
and String.prototype.endsWith()
const string = "Hello World!";
console.log(string.startsWith("Hello")); // true
console.log(string.endsWith("!")); // true
These two methods can be useful in particular cases, and it may happen that you have to use them more than you might think. But if you want to do a generic search, it’s better to use another solution.
Conclusions
Well, I would say that these ten solutions are enough. Beyond the entertainment related to this specific case, it is always worth dedicating some time to explore the various solutions available. The first idea is not always the best. And, in general, it’s a good way to learn more about a topic and improve your skills.