7407

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?

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
gramm
  • 18,786
  • 7
  • 27
  • 27

3 Answers3

15736

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
Yogesh Naik
  • 102
  • 8
Fabien Ménager
  • 140,109
  • 3
  • 41
  • 60
  • 49
    While 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) search. – Gavin Jun 18 '21 at 15:22
  • 8
    @Aashiq: Yes, an empty string is a substring of every string. – Ry- Sep 22 '21 at 15:39
  • 9
    @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) – Davo Jan 15 '22 at 01:31
  • 1
    `indexOf` is also case case-sensitive search, so both `includes` and `indexOf` are case-sensitive . – Experimenter Apr 13 '22 at 00:21
  • 9
    Why is a discussion of case sensitivity even taking place here? – KWallace Jun 30 '22 at 19:04
  • what is the complexity of javascript implementation of string.indexOf()? – Mitul Jul 10 '22 at 11:34
  • In 'C' a string is just an array of char. Is there a solution for strstr on an array of any type of object? Let's say of a specific type, as in Uint8Array? – Max Waterman Sep 02 '22 at 12:34
  • @MaxWaterman: Nothing built-in. Options include converting to a string (`String.fromCharCode(...array).includes(String.fromCharCode(...subarray))`) or implementing a string search algorithm for arrays (e.g. KMP as seen in another answer here). – Ry- Sep 09 '22 at 15:20
  • @Ry- Shame. I know about solutions in JavaScript, but they all (seem to) involve copying the array or implementing a search JS-side, which is what we're trying to avoid. It seems like a curious omission :/ – Max Waterman Sep 10 '22 at 16:34
  • the funnys thing about `.includes()`; Previously it name was `.contains()` like `C#`, it name was proposed for many years and maybe since javascript born, but people with high authority in JS hate so much MSoft so it renamed to `includes` – uingtea Oct 03 '22 at 14:13
  • @uingtea: [wrong](https://esdiscuss.org/topic/having-a-non-enumerable-array-prototype-contains-may-not-be-web-compatible) – Ry- Oct 03 '22 at 21:19
  • @KWallace a significant amount of freshly hatched newblet developers are coming from a case-insensitive-by-default world. It's overwhelmingly and increasingly common in higher-level UI/UX to do things case-insensitive by default. To you and me and the rest of us lost-in-the-sauce fluent-in-programming nerds, it's so obvious that case sensitivity is expected that "is this a substring of that?" automatically implies an assumption of case sensitivity without a conscious question/thought that it could be otherwise, but to anyone who hasn't yet learned to expect that, it really is worth mentioning. – mtraceur Jul 25 '23 at 16:59
779

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;
    }
  };
}
Zoe
  • 27,060
  • 21
  • 118
  • 148
eliocs
  • 18,511
  • 7
  • 40
  • 52
  • 1
    just curious, why do you need to check the length? Does IE fail in that case or something? – gman Feb 02 '21 at 15:29
  • 2
    Also the checking for `number` fails to perform like `includes`. Example: es6 includes returns false for `"abc".includes("ab", "1")` this polyfill will return true – gman Feb 02 '21 at 15:34
105

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(nm) 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
Johnny
  • 1,063
  • 1
  • 11
  • 23
wz366
  • 2,840
  • 6
  • 25
  • 34
  • 8
    Not questioning anything on this approach... but why implementing KMP where there's a `includes` or `indexOf` on the table. (Although the underneath impl of those maybe using KMP... not sure) – sphoenix Jul 13 '21 at 17:00
  • 2
    KMP provides linear O(n) performance here. – wz366 Jul 15 '21 at 17:20
  • 1
    @wz366 KMP provides O(n), what about the rest? Any Idea? – TheLebDev Jul 18 '21 at 08:12
  • 3
    If this is used for speed, it would likely run faster if you replaced `.charAt(i)` with `[i]` to avoid the extra function calls. – dandavis Aug 20 '21 at 02:54
  • 2
    99% of the times, doing this is overkill and harmful for non-computational aspects of a software project. Unless you're working on something extremely critical, I strongly advise against following this path... I don't think that big players like Twitter, Facebook, or most of Google products would even use this... so why should you? – Clint Eastwood Jan 11 '23 at 19:16
  • 1
    @ClintEastwood: This same approach works on things that aren’t JavaScript strings, which can be useful in all sorts of applications. (You should probably import it from a package if you need it, though, instead of embedding a copy from a Stack Overflow answer.) “Extremely critical” isn’t the right distinction. – Ry- Jan 11 '23 at 21:29