405

In SQL we can see if a string is in a list like so:

Column IN ('a', 'b', 'c')

What's a good way to do this in JavaScript? It's so clunky to do this:

if (expression1 || expression2 || str === 'a' || str === 'b' || str === 'c') {
   // do something
}

And I'm not sure about the performance or clarity of this:

if (expression1 || expression2 || {a:1, b:1, c:1}[str]) {
   // do something
}

Or one could use the switch function:

var str = 'a',
   flag = false;

switch (str) {
   case 'a':
   case 'b':
   case 'c':
      flag = true;
   default:
}

if (expression1 || expression2 || flag) {
   // do something
}

But that is a horrible mess. Any ideas?

In this case, I have to use Internet Explorer 7 as it's for a corporate intranet page. So ['a', 'b', 'c'].indexOf(str) !== -1 won't work natively without some syntax sugar.

ErikE
  • 48,881
  • 23
  • 151
  • 196
  • 2
    Could you explain what exactly is the difference between "string is in list" and "array includes an object"? – Michał Perłakowski Jan 06 '17 at 21:40
  • 2
    @Gothdo Because a list is not always an array, and a string is not an object? How could it be clearer? – ErikE Jan 06 '17 at 23:04
  • @ErikE if this is the case what you mentioned in **NOTE** then this question should be closed there should not be any further bounty/answers allowed. Already posted answers are sufficient for anyone to get help. – Vikasdeep Singh Jun 21 '18 at 04:56
  • @VikasdeepSingh Just because two questions have nearly-identical answers, does not mean they are the same question. "How can I save my brother's life when he's got arterial bleeding from the leg?" and "How can I viciously cause great bodily harm to my healthy brother" might both be answered by "put a tourniquet on his leg". But a person asking the first question is never going to search for, and find, the second question... – ErikE Feb 10 '23 at 23:22

15 Answers15

487

ES6 (ES2015) and up

If you're using ECMAScript 6 (a.k.a. ES2015) or higher, the cleanest way is to construct an array of the items and use Array.includes:

['a', 'b', 'c'].includes('b')

This has some inherent benefits over indexOf because it can properly test for the presence of NaN in the list, and can match missing array elements such as the middle one in [1, , 2] to undefined. It also treats +0 and -0 as equal. includes also works on JavaScript typed arrays such as Uint8Array.

If you're concerned about browser support (such as for IE or Edge), you can check Array.includes at CanIUse.Com, and if you want to target a browser or browser version that's missing includes, you'll need to transpile to a lower ECMAScript version using a tool such as Babel, or include a polyfill script in the browser, such as those available at polyfill.io.

Higher Performance

Note that there is no guarantee that Array.includes() execution time won't scale with the number of elements in the array: it can have performance O(n). If you need higher performance, and won't be constructing the set of items repeatedly (but will be repeatedly checking if the items contain some element), you should use a Set because the ES spec requires implementations of Set (and Map as well) to be sub-linear for reads:

The specification requires sets to be implemented "that, on average, provide access times that are sublinear on the number of elements in the collection". Therefore, it could be represented internally as a hash table (with O(1) lookup), a search tree (with O(log(N)) lookup), or any other data structure, as long as the complexity is better than O(N).

const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')

Note that you can pass in any iterable item to the Set constructor (anything that supports for...of). You can also convert a Set to an array using Array.from(set) or by spreading it [...set].

Without An Array

This is not really recommended, but you could add a new isInList property to strings as follows:

if (!String.prototype.isInList) {
  Object.defineProperty(String.prototype, 'isInList', {
    get: () => function(...args) {
      let value = this.valueOf();
      for (let i = 0, l = args.length; i < l; i += 1) {
        if (arguments[i] === value) return true;
      }
      return false;
    }
  });
}

Then use it like so:

'fox'.isInList('weasel', 'fox', 'stoat') // true
'fox'.isInList('weasel', 'stoat') // false

You can do the same thing for Number.prototype.

Note that Object.defineProperty cannot be used in IE8 and earlier, or very old versions of other browsers. However, it is a far superior solution to String.prototype.isInList = function() { ... } because using simple assignment like that will create an enumerable property on String.prototype, which is more likely to break code.

Array.indexOf

If you are using a modern browser, indexOf always works. However, for IE8 and earlier you'll need a polyfill.

If indexOf returns -1, the item is not in the list. Be mindful though, that this method will not properly check for NaN, and while it can match an explicit undefined, it can’t match a missing element to undefined as in the array [1, , 2].

Polyfill for indexOf or includes in IE, or any other browser/version lacking support

If you don't want to use a service like polyfill.io as mentioned above, you can always include in your own source code standards-compliant custom polyfills. For example, the CoreJs library has an implementation of indexOf.

In this situation where I had to make a solution for Internet Explorer 7, I "rolled my own" simpler version of the indexOf() function that is not standards-compliant:

if (!Array.prototype.indexOf) {
   Array.prototype.indexOf = function(item) {
      var i = this.length;
      while (i--) {
         if (this[i] === item) return i;
      }
      return -1;
   }
}

Notes On Modifying Object Prototypes

However, I don't think modifying String.prototype or Array.prototype is a good strategy long term. Modifying object prototypes in JavaScript can lead to serious bugs. You need to decide whether doing so is safe in your own environment. Of primary note is that iterating an array (when Array.prototype has added properties) with for ... in will return the new function name as one of the keys:

Array.prototype.blah = function() { console.log('blah'); };
let arr = [1, 2, 3];
for (let x in arr) { console.log(x); }
// Result:
0
1
2
blah // Extra member iterated over!

Your code may work now, but the moment someone in the future adds a third-party JavaScript library or plugin that isn't zealously guarding against inherited keys, everything can break.

The old way to avoid that breakage is, during enumeration, to check each value to see if the object actually has it as a non-inherited property with if (arr.hasOwnProperty(x)) and only then work with that x.

The new ES6 ways to avoid this extra-key problem are:

  1. Use of instead of in, for (let x of arr). However, depending on the output target and the exact settings/capabilities of your down-leveling transpiler, this may not be reliable. Plus, unless you can guarantee that all of your code and third-party libraries strictly stick to this method, then for the purposes of this question you'll probably just want to use includes as stated above.

  2. Define your new properties on the prototype using Object.defineProperty(), as this will make the property (by default) non-enumerable. This only truly solves the problem if all the JavaScript libraries or modules you use also do this.

Be Aware of One Last Issue

Last, be aware that while polyfills make sense, and modifying object prototypes is a useful strategy, there can occasionally still be scoping problems with that approach.

In a browser, each distinct document object is its own new global scope, and in browser JS it is possible to create new documents (such as those used for off-screen rendering or to create document fragments) or to get a reference to another page's document object (such as via inter-page communication using a named-target link) so it's possible in certain (rare?) circumstances that object prototypes won't have the methods you expect them to have—though you could always run your polyfills again against the new global objects...

In Node.js, modifying prototypes of global objects may be safe, but modifying the prototypes of non-global, imported objects could lead to breakage if you ever end up with two versions of the same package being required/imported, because imports of the two versions will not expose the same objects, thus won't have the same object prototypes. That is, your code could work fine until a dependency or sub-dependency uses a different version from the one you expect, and without any of your own code changing, a simple npm install or yarn install could trigger this problem. (There are options to deal with this, such as yarn's resolutions property in the package.json, but that's not a good thing to rely on if you have other options.)

ErikE
  • 48,881
  • 23
  • 151
  • 196
  • 2
    here's another good [PolyFill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill) for `indexOf` provided by MDN. It basically does the same thing but with a couple of short circuits for easy evaluation. – KyleMit Jan 21 '14 at 21:24
  • 1
    Downvoter: please comment. What's the issue with this? This function is NOT standards-compliant, and isn't trying to be. – ErikE May 19 '15 at 21:43
  • You should use something already tested like the polyfill noted by @KyleMit link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill – lmiguelmh Mar 07 '16 at 15:00
  • @lmiguelmh How about the "polyfill" that I myself posted a link to, from the Mozilla docs themselves? In any case, this function is so dead simple that I'm really not too concerned about testing. And anyone who is, shouldn't take an "already tested" function but should test whatever they use, themselves. So your comment is a bit misplaced. Have you identified a specific defect with my function? – ErikE Mar 07 '16 at 15:28
  • @ErikE you right this function is dead and anyone using your answer should know it. I didn't see your link in the first place because I was looking for "answers" – lmiguelmh Mar 07 '16 at 15:35
327

You can call indexOf:

if (['a', 'b', 'c'].indexOf(str) >= 0) {
    //do something
}
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 17
    The only problem with `Array.prototype.indexOf` is that it will not work on IE, sadly even IE8 lacks this method. – Christian C. Salvadó Mar 12 '10 at 02:24
  • 2
    But you can define it if you want to. See http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/ – Jason Hall Mar 12 '10 at 02:35
  • Here are the "real" goods: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/indexOf – ErikE Mar 12 '10 at 02:42
  • 8
    @ImJasonH: The code on that page is really really bad IMHO, for example, the check if `Array.indexOf` exists before overriding `Array.prototype.indexOf` which are *not* the same thing. I would recommend the implementation made by Mozilla available here: https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/IndexOf – Christian C. Salvadó Mar 12 '10 at 02:43
  • 1
    Listen to @CMS, @Emtucifor, the Mozilla implementation is a lot better. – Jason Hall Mar 12 '10 at 04:32
  • I would do `!= -1` instead of `>= 0` for efficiency's sake – Omar Himada Jun 21 '18 at 17:50
  • One potential pitfall of this solution is that it `indexOf` won't match on `NaN` or `undefined`. – ErikE Jul 19 '18 at 16:50
  • @ErikE: It will match `undefined`, but not `NaN`. – SLaks Jul 19 '18 at 16:54
  • @SLaks Let me clarify. `indexOf` will find explicit `undefined` values, but not missing ones as in `[1, ,2].indexOf(undefined)` result: `-1`. – ErikE Jul 20 '18 at 02:09
55

Most of the answers suggest the Array.prototype.indexOf method, the only problem is that it will not work on any IE version before IE9.

As an alternative I leave you two more options that will work on all browsers:

if (/Foo|Bar|Baz/.test(str)) {
  // ...
}


if (str.match("Foo|Bar|Baz")) {
  // ...
}
ErikE
  • 48,881
  • 23
  • 151
  • 196
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • Hmmm, thanks for mentioning that, CMS. It just so happens that this is for a corporate intranet and they use... guess what... IE. Since the regular expression method gives me the willies, I'll either have to make a function that loops, or use the object method I suggested in my post (third code block). – ErikE Mar 12 '10 at 02:26
  • 15
    This will match `"HiFooThere"` - I'd go with `/^(?:Foo|Bar|Baz)$/` instead (beginning of string, non-capturing group, end of string). – TrueWill Aug 13 '15 at 13:47
  • `indexOf` is now supported in IE 9 and above according to [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf). – krock Dec 09 '15 at 05:16
32

Arrays have an indexOf method which can be used to search for strings:

js> a = ['foo', 'bar', 'baz']
foo,bar,baz
js> a.indexOf('bar')
1
js> a.indexOf('quux')
-1
harto
  • 89,823
  • 9
  • 47
  • 61
18

In addition to indexOf (which other posters have suggested), using prototype's Enumerable.include() can make this more neat and concise:

var list = ['a', 'b', 'c'];
if (list.includes(str)) {
  // do stuff
}
Buddy Bob
  • 5,829
  • 1
  • 13
  • 44
pkaeding
  • 36,513
  • 30
  • 103
  • 141
  • 8
    Enumerable.include() has been deprecated, but [Array.prototype.includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) is coming, and is already working in most browsers except IE (of course) and Edge (of course). – whitebeard Jan 18 '16 at 04:28
14

A trick I've used is

>>> ("something" in {"a string":"", "somthing":"", "another string":""})
false
>>> ("something" in {"a string":"", "something":"", "another string":""})
true

You could do something like

>>> a = ["a string", "something", "another string"];
>>> b = {};
>>> for(var i=0; i<a.length;i++){b[a[i]]="";} /* Transform the array in a dict */
>>> ("something" in b)
true
Esteban Küber
  • 36,388
  • 15
  • 79
  • 97
  • voyager, is using "in" any faster/slower/better/worse than just attempting to dereference the item as my third code block in my question showed? Also, if you're going to loop through the thing, I figure you may as well check if the element's in the array at that time... just wrap the loop in a function. And for what it's worth `var i=a.length;while (i--) {/*use a[i]*/}` is the fastest loop method (if reverse order is acceptable). – ErikE Mar 12 '10 at 02:31
  • @Emtucifor: it really depends on what you are doing, and I guess that it might work differently on different javascript engines. If your data would need at any point the use of a dictionary, then it is better to create it this way. I'd think that this will be faster because of implementation details on the engines (the use of hash tables for object) *once the dict object is created*. – Esteban Küber Mar 12 '10 at 02:38
  • In JavaScript, it's not called a dict, it's called an *object*. – Solomon Ucko May 07 '16 at 19:28
10

Using indexOf(it doesn’t work with IE8).

if (['apple', 'cherry', 'orange', 'banana'].indexOf(value) >= 0) {
    // found
}

To support IE8, you could implement Mozilla’s indexOf.

if (!Array.prototype.indexOf) {
    // indexOf polyfill code here
}

Regular Expressions via String.prototype.match (docs).

if (fruit.match(/^(banana|lemon|mango|pineapple)$/)) {

}
Tiago Medici
  • 1,944
  • 22
  • 22
8

Here's mine:

String.prototype.inList=function(list){
    return (Array.apply(null, arguments).indexOf(this.toString()) != -1)
}

var x = 'abc';
if (x.inList('aaa','bbb','abc'))
    console.log('yes');
else
    console.log('no');

This one is faster if you're OK with passing an array:

String.prototype.inList=function(list){
    return (list.indexOf(this.toString()) != -1)
}

var x = 'abc';
if (x.inList(['aaa','bbb','abc']))
    console.log('yes')

Here's the jsperf: http://jsperf.com/bmcgin-inlsit

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brian McGinity
  • 5,777
  • 5
  • 36
  • 46
  • 1
    Frankly, the one where you pass an array would probably be more useful, and it should probably be on `Array`'s prototype: maybe something like `Array.prototype.contains`. – Solomon Ucko May 07 '16 at 19:30
7

RegExp is universal, but I understand that you're working with arrays. So, check out this approach. I use to use it, and it's very effective and blazing fast!

var str = 'some string with a';
var list = ['a', 'b', 'c'];
var rx = new RegExp(list.join('|'));

rx.test(str);

You can also apply some modifications, i.e.:

One-liner

new RegExp(list.join('|')).test(str);

Case insensitive

var rx = new RegExp(list.join('|').concat('/i'));


And many others!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sospedra
  • 14,238
  • 3
  • 21
  • 32
  • Using regex requires avoiding a bunch of special characters. It also is less clear. I don't think it's a good solution. – ErikE Mar 06 '15 at 23:09
  • @ErikE I understand your reservations so I updated my answer adding another code which doesn't use RegExp, it's retrocompatible with IE, very idiomatic and fast. – sospedra Jan 18 '16 at 15:16
  • Your additional code is a near-duplicate of my answer which I provided 5 years before you decided to post a Regex solution. Thank you for participating, and at the same time I think rolling back to your prior answer is best. – ErikE Jan 18 '16 at 15:57
  • @ErikE yep, you're right, I don't use to check for answers in the question. And I agree it's really similar. – sospedra Jan 19 '16 at 12:00
5

Looks like you need to use in_array function.

jQuery -> inArray

Prototype -> Array.indexOf

Or, see these examples if you are not using jQuery or Prototype:

Stylistic note: variables named thisthing thatthing, should be named to tell you something about what they contain (noun).

LG_PDX
  • 834
  • 8
  • 12
  • Oh, they weren't variables but were meant as random placeholders for expressions... just an example of how I planned to use the script. – ErikE Mar 12 '10 at 02:13
2

Thanks for the question, and the solution using the Array.indexOf method.

I used the code from this solution to create a inList() function that would, IMO, make the writing simpler and the reading clearer:

function inList(psString, psList) 
{
    var laList = psList.split(',');

    var i = laList.length;
    while (i--) {
        if (laList[i] === psString) return true;
    }
    return false;
}

USAGE:

if (inList('Houston', 'LA,New York,Houston') {
  // THEN do something when your string is in the list
}
BenMorel
  • 34,448
  • 50
  • 182
  • 322
JMichaelTX
  • 1,659
  • 14
  • 19
  • 1
    Javascript array literals are so easy, I don't see why you would split when you could do `'Houston'.inList(['LA', 'New York', 'Houston'])`. Perhaps `if (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}` or using your `while` method. – ErikE Aug 27 '11 at 07:28
1

My solution results in a syntax like this:

// Checking to see if var 'column' is in array ['a', 'b', 'c']

if (column.isAmong(['a', 'b', 'c']) {
  // Do something
}

And I implement this by extending the basic Object prototype, like this:

Object.prototype.isAmong = function (MyArray){
   for (var a=0; a<MyArray.length; a++) {
      if (this === MyArray[a]) { 
          return true;
      }
   }
   return false;
}

We might alternatively name the method isInArray (but probably not inArray) or simply isIn.

Advantages: Simple, straightforward, and self-documenting.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
John Hicks
  • 41
  • 3
  • There could be trouble extending Object. http://bolinfest.com/javascript/inheritance.php under "The Google Maps team learned this the hard way" and incompatibility with browser implementation or other user's code. I still think ErikE's answer is the best one since iterating over an array is slower than finding a key in a hashmap once the hashmap is created: `myValues[key];` where myValues is an object and key is any string or number. – HMR Nov 12 '13 at 05:38
0

I'm surprised no one had mentioned a simple function that takes a string and a list.

function in_list(needle, hay)
{
    var i, len;

    for (i = 0, len = hay.length; i < len; i++)
    {
        if (hay[i] == needle) { return true; }
    }

    return false;
}

var alist = ["test"];

console.log(in_list("test", alist));
Samuel Parkinson
  • 2,992
  • 1
  • 27
  • 38
  • Jim [did exactly what you suggest](http://stackoverflow.com/a/7211984/57611), Sam, on Aug 27 '11 at 1:29. In fact, my selected answer is pretty much the same thing, just supplying the string with *this* rather than a parameter. – ErikE Sep 20 '12 at 09:51
  • @ErikE Sorry, Jims answer seemed odd to me as you said. And your accepted answer returns an `int`, where as some people might come across this question looking for a `bool` return. Figured it might help a few people. – Samuel Parkinson Sep 20 '12 at 17:54
0

A simplified version of SLaks' answer also works:

if ('abcdefghij'.indexOf(str) >= 0) {
    // Do something
}

....since strings are sort of arrays themselves. :)

If needed, implement the indexof function for Internet Explorer as described before me.

Community
  • 1
  • 1
Angezerus
  • 45
  • 4
-1

My little contribution:

function fnListIndexOf(pList, pValue)
{
    return pList.split(",").indexOf (pValue);
}

fnListIndexOf("1,2,3,4,5,a,b,c","a")