How to check whether a string contains a substring in JavaScript?
Solution 1
ECMAScript 6 introduced String.prototype.includes
:
const string = "foo";
const substring = "oo";
console.log(string.includes(substring)); // true
includes
doesn’t have Internet Explorer support, though. In ECMAScript 5 or older environments, use String.prototype.indexOf
, which returns -1 when a substring cannot be found:
var string = "foo";
var substring = "oo";
console.log(string.indexOf(substring) !== -1); // true
Solution 2
There is a String.prototype.includes
in ES6:
"potato".includes("to");
> true
Note that this does not work in Internet Explorer or some other old browsers with no or incomplete ES6 support. To make it work in old browsers, you may wish to use a transpiler like Babel, a shim library like es6-shim, or this polyfill from MDN:
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;
}
};
}
Solution 3
Another alternative is KMP (Knuth–Morris–Pratt).
The KMP algorithm searches for a length-m substring in a length-n string in worst-case O(n+m) time, compared to a worst-case of O(n⋅m) for the naive algorithm, so using KMP may be reasonable if you care about worst-case time complexity.
Here's a JavaScript implementation by Project Nayuki, taken from https://www.nayuki.io/res/knuth-morris-pratt-string-matching/kmp-string-matcher.js:
// Searches for the given pattern string in the given text string using the Knuth-Morris-Pratt string matching algorithm.
// If the pattern is found, this returns the index of the start of the earliest match in 'text'. Otherwise -1 is returned.
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
gramm
Updated on March 09, 2022Comments
-
gramm about 2 years
Usually I would expect a
String.contains()
method, but there doesn't seem to be one.What is a reasonable way to check for this?
-
gman over 3 yearsjust curious, why do you need to check the length? Does IE fail in that case or something?
-
gman over 3 yearsAlso the checking for
number
fails to perform likeincludes
. Example: es6 includes returns false for"abc".includes("ab", "1")
this polyfill will return true -
Gavin almost 3 yearsWhile this is a good answer, and the OP never requested for a "case-sensitive" search, it should be noted that
includes
performs a case-sensitive search. -
sphoenix almost 3 yearsNot questioning anything on this approach... but why implementing KMP where there's a
includes
orindexOf
on the table. (Although the underneath impl of those maybe using KMP... not sure) -
wz366 almost 3 yearsKMP provides linear O(n) performance here.
-
TheLebDev almost 3 years@wz366 KMP provides O(n), what about the rest? Any Idea?
-
dandavis over 2 yearsIf this is used for speed, it would likely run faster if you replaced
.charAt(i)
with[i]
to avoid the extra function calls. -
Aashiq over 2 yearsincludes return true for empty substring .
-
Ry- over 2 years@Aashiq: Yes, an empty string is a substring of every string.
-
Davo over 2 years@Gavin by default if I want to know if something is a substring, I imagine it would be case-sensitive. After all, "A" and "a" are different characters. The OP never requested a "case-insensitive" search ( which is a trivial solution, if you make everything lowercase)
-
Experimenter about 2 years
indexOf
is also case case-sensitive search, so bothincludes
andindexOf
are case-sensitive . -
KWallace almost 2 yearsWhy is a discussion of case sensitivity even taking place here?