判断字符串是否在JavaScript列表中

在SQL中,我们可以看到一个字符串是否像这样在一个列表中:

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

JavaScript中有什么好方法呢?这样做太笨拙了:

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

我不确定它的表现和清晰度:

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

或者可以使用switch函数:

var str = 'a',
flag = false;


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


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

但这是一个可怕的混乱。什么好主意吗?

在这种情况下,我必须使用internet Explorer 7,因为它是用于公司内部网页面的。因此,如果没有一些语法糖,['a', 'b', 'c'].indexOf(str) !== -1将无法本机工作。

496959 次浏览

数组有一个indexOf方法,可用于搜索字符串:

js> a = ['foo', 'bar', 'baz']
foo,bar,baz
js> a.indexOf('bar')
1
js> a.indexOf('quux')
-1

你可以调用indexOf:

if (['a', 'b', 'c'].indexOf(str) >= 0) {
//do something
}

除了indexOf(这是其他海报建议的),使用prototype的Enumerable.include ()可以使其更加简洁:

var list = ['a', 'b', 'c'];
if (list.includes(str)) {
// do stuff
}

看起来你需要使用in_array函数。

jQuery -> inArray

Prototype -> Array.indexOf

或者,如果你不使用jQuery或Prototype,看看这些例子:

< em >风格注:< / em >变量命名为thisthing thatthing,应该命名为告诉你他们包含什么(名词)。

大多数答案建议使用Array.prototype.indexOf方法,唯一的问题是它不能在IE9之前的任何 IE版本上工作。

作为替代,我给你留下了两个可以在所有浏览器上工作的选项:

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




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

我用过的一个技巧是

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

你可以这样做

>>> 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

ES6 (ES2015)及以上版本

如果你使用的是ECMAScript 6(也就是ES2015)或更高版本,最简洁的方法是构造一个元素数组并使用Array.includes:

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

这比indexOf有一些固有的好处,因为它可以正确地测试列表中是否存在NaN,并且可以将缺少的数组元素(例如[1, , 2]中的中间元素)匹配到undefined。它还将+0-0视为相等。includes也适用于JavaScript类型数组,例如Uint8Array

如果你关心浏览器的支持(比如IE或Edge),你可以在CanIUse.Com上检查Array.includes,如果你想瞄准一个缺少includes的浏览器或浏览器版本,你需要使用Babel等工具编译到一个较低的ECMAScript版本,或者在浏览器中包含一个polyfill脚本,比如在polyfill.io中可用的脚本。

更高的性能

注意,不能保证Array.includes()的执行时间不随数组中元素的数量而缩放:它的性能可以是O(n)。如果你需要更高的性能,并且不会重复构造项目集(但会重复检查项目是否包含某些元素),你应该使用Set,因为ES规范要求Set(以及Map)的实现是子线性的读取:

规范要求实现的集合“平均而言,提供的访问时间与集合中元素的数量呈次线性关系”。因此,它可以在内部表示为哈希表(具有O(1)查找)、搜索树(具有O(log(N))查找)或任何其他数据结构,只要复杂度优于O(N)。

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

注意,可以将任何可迭代项传递给Set构造函数(任何支持的……的构造函数)。还可以使用Array.from(set)或将其展开为[...set]Set转换为数组。

没有数组

不建议这样做,但你可以像下面这样为字符串添加一个新的isInList属性:

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;
}
});
}

然后像这样使用它:

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

你可以为Number.prototype做同样的事情。

请注意,Object.defineProperty不能在IE8及更早版本或其他非常旧的浏览器中使用。然而,这是一个比String.prototype.isInList = function() { ... }更好的解决方案,因为使用这样简单的赋值将在String.prototype上创建一个可枚举的属性,这更有可能破坏代码。

Array.indexOf

如果你使用的是现代浏览器,indexOf总是有效的。然而,对于IE8和更早的版本,你需要一个polyfill。

如果indexOf返回-1,则该项不在列表中。不过要注意,这个方法不会正确地检查NaN,虽然它可以匹配显式的undefined,但它不能将缺少的元素匹配到数组[1, , 2]中的undefined

为IE中的indexOfincludes填充,或任何其他缺乏支持的浏览器/版本

如果你不想像上面提到的那样使用polyfill.io这样的服务,你总是可以在你自己的源代码中包含符合标准的自定义填充。例如,Mozilla Developer Network为indexOf提供了一个。

在这种情况下,我必须为Internet Explorer 7制定解决方案,我“自己动手”。不符合标准的indexOf()函数的简单版本:

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

关于修改对象原型的注释

然而,从长远来看,我不认为修改String.prototypeArray.prototype是一个好策略。在JavaScript中修改对象原型可能会导致严重的错误。你需要决定这样做在你自己的环境中是否安全。主要注意的是迭代一个数组(当array。prototype添加了属性)使用for ... in将返回新的函数名作为键之一:

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!

您的代码现在可能可以工作,但一旦将来有人添加了第三方JavaScript库或插件,而这些库或插件并没有热心地保护继承密钥,那么一切都可能被破坏。

避免这种破坏的旧方法是,在枚举过程中,检查每个值,看看对象是否确实将其作为if (arr.hasOwnProperty(x))只有这样与该x工作的非继承属性。

新的ES6方法来避免这个额外的关键问题:

  1. 使用of代替infor (let x of arr)。然而,根据输出目标和降级转译器的确切设置/能力,这可能不可靠。另外,除非你能保证你的所有代码和第三方库都严格遵守这个方法,否则为了解决这个问题,你可能只需要像上面说的那样使用includes

  2. 使用Object.defineProperty()在原型上定义你的新属性,因为这将使属性(默认情况下)不可枚举。只有当您使用的所有JavaScript库或模块也这样做时,这才真正解决了问题。

注意最后一个问题

最后,要注意,虽然填充是有意义的,修改对象原型是一个有用的策略,但这种方法偶尔仍然会出现范围问题。

在浏览器中,每个不同的document对象都是它自己的新全局作用域,在浏览器JS中,可以创建新的文档(比如那些用于屏幕外渲染或创建文档片段的文档)或获得对另一个页面的document对象的引用(比如通过使用命名目标链接的页面间通信),因此在某些(很少?)情况下,对象原型可能不具有您期望的方法-尽管您总是可以对新的全局对象再次运行您的polyfills…

在Node.js中,修改global对象的原型可能是安全的,但修改非全局导入对象的原型可能会导致破坏,如果你最终需要/导入同一个包的两个版本,因为两个版本的导入不会公开相同的对象,因此不会有相同的对象原型。也就是说,你的代码可以正常工作,直到一个依赖项或子依赖项使用了与你期望的不同的版本,并且在你自己的代码没有任何改变的情况下,一个简单的npm installyarn install可能会触发这个问题。(有一些选项可以处理这个问题,比如yarn在package.json中的resolutions属性,但如果你有其他选项,这不是一个好选择。)

谢谢你的问题,以及使用数组的解决方案。indexOf方法。

我使用这个解决方案的代码创建了一个inList()函数,在我看来,这将使写作更简单,阅读更清晰:

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


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

用法:

if (inList('Houston', 'LA,New York,Houston') {
// THEN do something when your string is in the 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));

SLaks的回答的简化版本也可以工作:

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

....因为字符串本身就是数组。:)

如果需要,实现Internet Explorer的indexof函数。

我的解决方案的结果是这样的语法:

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


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

我通过扩展基本的Object原型来实现这一点,像这样:

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

我们也可以将该方法命名为isInArray(但可能不是inArray)或简单地命名为isIn。

优点:简单、直接、自文档化。

这是我的:

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');

如果你可以传递一个数组,这个会更快:

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


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

下面是jsperf: http://jsperf.com/bmcgin-inlsit

RegExp是通用的,但我知道你正在使用数组。所以,看看这个方法。我过去经常用它,它非常有效,而且非常快!

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


rx.test(str);

您还可以应用一些修改,即:

一行程序

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

不分大小写

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


还有很多其他的!

使用indexOf(它不支持IE8)。

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

为了支持IE8,你可以实现Mozilla的indexOf。

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

通过String.prototype.match (docs)实现正则表达式。

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


}

我的小小贡献:

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


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