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?
ECMAScript 6 introduced String.prototype.includes
:
const string = "foo";
const substring = "oo";
console.log(string.includes(substring)); // true
String.prototype.includes
is case-sensitive and is not supported by Internet Explorer without a polyfill.
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
includes
performs a case-sensitive search.
indexOf
is also case case-sensitive search, so both includes
and indexOf
are case-sensitive .
Commented
Apr 13, 2022 at 0:21
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;
}
};
}
number
fails to perform like includes
. Example: es6 includes returns false for "abc".includes("ab", "1")
this polyfill will return true
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
includes
or indexOf
on the table. (Although the underneath impl of those maybe using KMP... not sure)
.charAt(i)
with [i]
to avoid the extra function calls.