Determine if string is in list in JavaScript
JavascriptStringListJavascript Problem Overview
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.
Javascript Solutions
Solution 1 - Javascript
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 Array.includes()
scales in its time with the number of elements in the array: it has 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
.
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)
.
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]
.
indexOf
or includes
in IE, or any other browser/version lacking support
Polyfill for 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, Mozilla Developer Network has one for 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:
-
Use
of
instead ofin
,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 useincludes
as stated above. -
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, 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.)
Solution 2 - Javascript
You can call indexOf
:
if (['a', 'b', 'c'].indexOf(str) >= 0) {
//do something
}
Solution 3 - Javascript
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")) {
// ...
}
Solution 4 - Javascript
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
Solution 5 - Javascript
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
Solution 6 - Javascript
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
}
Solution 7 - Javascript
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
Solution 8 - Javascript
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!
Solution 9 - Javascript
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)$/)) {
}
Solution 10 - Javascript
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:
- http://phpjs.org/functions/in_array:432
- http://www.bitrepository.com/equivalent-of-phps-in_array-function.html
- http://codingforums.com/showthread.php?t=63796
Stylistic note: variables named thisthing thatthing, should be named to tell you something about what they contain (noun).
Solution 11 - Javascript
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
}
Solution 12 - Javascript
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));
Solution 13 - Javascript
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.
Solution 14 - Javascript
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.
Solution 15 - Javascript
My little contribution:
function fnListIndexOf(pList, pValue)
{
return pList.split(",").indexOf (pValue);
}
fnListIndexOf("1,2,3,4,5,a,b,c","a")