确定字符串是不是在 JavaScript 的列表中
Posted
技术标签:
【中文标题】确定字符串是不是在 JavaScript 的列表中【英文标题】:Determine if string is in list in JavaScript确定字符串是否在 JavaScript 的列表中 【发布时间】:2011-01-26 15:56:48 【问题描述】:在 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
或者可以使用开关功能:
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
将无法在本机工作。
【问题讨论】:
你能解释一下“字符串在列表中”和“数组包含一个对象”之间的区别吗? @Gothdo 因为列表并不总是数组,字符串也不是对象?怎么可能更清楚? @ErikE 如果是您在 NOTE 中提到的情况,那么这个问题应该被关闭,不应该有任何进一步的赏金/答案。已经发布的答案足以让任何人获得帮助。 【参考方案1】:数组有一个indexOf
方法,可用于搜索字符串:
js> a = ['foo', 'bar', 'baz']
foo,bar,baz
js> a.indexOf('bar')
1
js> a.indexOf('quux')
-1
【讨论】:
这将在旧浏览器上失败。 哎呀...提到这在 IE 中不起作用会很好。 :) @epascarello:不仅在旧版浏览器中,它会在 any IE 上失败,即使在 IE8 中也是如此:(【参考方案2】:您可以拨打indexOf
:
if (['a', 'b', 'c'].indexOf(str) >= 0)
//do something
【讨论】:
Array.prototype.indexOf
唯一的问题是它不能在 IE 上运行,遗憾的是即使 IE8 也没有这种方法。
但是你可以定义它,如果你想。见soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer
这里是“真货”:developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…
@ImJasonH:恕我直言,该页面上的代码真的很糟糕,例如,在覆盖Array.prototype.indexOf
之前检查Array.indexOf
是否存在,这不是同一件事.我会推荐 Mozilla 在这里提供的实现:developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/…
听@CMS,@Emtucifor,Mozilla 的实现要好得多。【参考方案3】:
除了indexOf
(其他发帖人建议的),使用prototype的Enumerable.include()可以让这个更简洁:
var list = ['a', 'b', 'c'];
if (list.includes(str))
// do stuff
【讨论】:
Enumerable.include() 已被弃用,但 Array.prototype.includes() 即将推出,并且已经在大多数浏览器中工作,除了 IE(当然)和 Edge(当然)。【参考方案4】:看起来你需要使用 in_array 函数。
jQuery -> inArray
原型 -> Array.indexOf
或者,如果您不使用 jQuery 或 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风格说明:变量命名为 thisthing thatthing,应该命名以告诉您它们包含的内容(名词)。
【讨论】:
哦,它们不是变量,而是作为表达式的随机占位符......只是我计划如何使用脚本的一个示例。【参考方案5】:大部分答案都建议使用Array.prototype.indexOf
方法,唯一的问题是它不适用于任何 IE9 之前的IE 版本。
作为替代方案,我为您提供了两个适用于所有浏览器的选项:
if (/Foo|Bar|Baz/.test(str))
// ...
if (str.match("Foo|Bar|Baz"))
// ...
【讨论】:
嗯,感谢您提及,CMS。碰巧这是针对公司内部网的,他们使用...猜猜看... IE。由于正则表达式方法给了我威力,我要么必须创建一个循环的函数,要么使用我在帖子中建议的对象方法(第三个代码块)。 这将匹配"HiFooThere"
- 我会使用 /^(?:Foo|Bar|Baz)$/
代替(字符串开头,非捕获组,字符串结尾)。
indexOf
现在支持 IE 9 及更高版本,根据MDN。【参考方案6】:
我用过的一个技巧是
>>> ("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
【讨论】:
voyager,使用“in”是否比我的问题中的第三个代码块显示的仅尝试取消引用该项目更快/更慢/更好/更差?另外,如果您要循环遍历该事物,我想您也可以检查该元素当时是否在数组中……只需将循环包装在一个函数中即可。值得var i=a.length;while (i--) /*use a[i]*/
是最快的循环方法(如果可以接受相反的顺序)。
@Emtucifor:这真的取决于你在做什么,我猜它在不同的 javascript 引擎上可能会有所不同。如果您的数据在任何时候都需要使用字典,那么最好以这种方式创建它。我认为这会更快,因为引擎上的实现细节(对象的哈希表的使用)一旦创建 dict 对象。
在 JavaScript 中,它不被称为 dict,它被称为 object。【参考方案7】:
EcmaScript 6 及更高版本
如果您使用的是 ES6 或更高版本,最简洁的方法是构造项目数组并使用 Array.includes
:
['a', 'b', 'c'].includes('b')
这与indexOf
相比具有一些固有的优势,因为它可以正确地测试列表中NaN
的存在,并且可以将缺少的数组元素(例如[1, , 2]
中的中间元素)匹配到undefined
。 includes
也适用于 JavaScript typed arrays,例如 Uint8Array
。
如果您担心浏览器支持(例如 IE 或 Edge),您可以 check Array.includes
at CanIUse.Com,如果您想定位缺少 includes
的浏览器或浏览器版本,我建议使用 polyfill.io 进行 polyfill .
更高的性能
请注意,Array.includes()
的时间随数组中元素的数量而变化:它的性能为 O(n)。如果您需要更高的性能,并且不会重复构建项目集(但会重复检查项目是否包含某些元素),则应使用Set
。
const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')
请注意,您可以将任何可迭代项传递给Set
构造函数(任何支持for...of 的东西)。您还可以使用Array.from(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
,但它无法将缺失的元素匹配到 undefined
,如数组 [1, , 2]
。
为 IE 中的 indexOf
或 includes
或任何其他缺乏支持的浏览器/版本进行 Polyfill
如果您不想使用上面提到的polyfill.io 之类的服务,您可以随时在自己的源代码中包含符合标准的自定义 polyfill。例如,Mozilla 开发者网络有一个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.prototype
或Array.prototype
不是长期的好策略。在 JavaScript 中修改对象原型可能会导致严重的错误。您需要决定这样做在您自己的环境中是否安全。需要注意的是,使用 for ... in
迭代数组(当 Array.prototype 添加了属性时)将返回新的函数名称作为键之一:
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 库或插件,而这些第三方 JavaScript 库或插件并未积极防范继承的密钥,一切都会崩溃。
避免这种破坏的旧方法是,在枚举期间,检查每个值以查看对象是否确实将其作为具有if (arr.hasOwnProperty(x))
的非继承属性,并且只有这样才能使用它x
.
避免这种额外密钥问题的新 ES6 方法是:
使用of
代替in
、for (let x of arr)
。但是,根据输出目标和下级转译器的确切设置/功能,这可能不可靠。另外,除非您可以保证您的所有代码和第三方库都严格遵守此方法,否则出于此问题的目的,您可能只想使用includes
,如上所述。
使用Object.defineProperty()
在原型上定义您的新属性,因为这将使该属性(默认情况下)不可枚举。只有当您使用的所有 JavaScript 库或模块都这样做时,才能真正解决问题。
注意最后一期
最后,请注意,虽然 polyfill 很有意义,并且修改对象原型是一种有用的策略,但这种方法偶尔仍会存在范围问题。
在浏览器中,每个不同的 document
对象都是它自己的新全局范围,在浏览器 JS 中可以创建新文档(例如用于离屏渲染或创建文档片段的文档)或获取对另一个页面的document
对象的引用(例如通过使用命名目标链接的页面间通信),因此在某些(罕见?)情况下,对象原型可能没有您期望它们具有的方法——尽管您总是可以针对新的全局对象再次运行您的 polyfill...
在节点中,修改 global
对象的原型可能是安全的,但是如果您最终需要/导入同一个包的两个版本,则修改非全局导入对象的原型可能会导致损坏,因为两个版本的导入不会暴露相同的对象,因此不会有相同的对象原型。也就是说,您的代码可以正常工作,直到依赖项或子依赖项使用与您期望的版本不同的版本,并且您自己的代码没有任何更改,一个简单的npm install
或yarn install
可能会触发此问题。 (有一些选项可以解决这个问题,例如 package.json
中 yarn 的 resolutions
属性,但如果您有其他选项,则依赖这不是一件好事。)
【讨论】:
这是另一个很好的 PolyFill 用于 MDN 提供的indexOf
。它基本上做同样的事情,但有几个短路以便于评估。
反对者:请发表评论。这有什么问题?此函数不符合标准,也没有尝试这样做。
你应该使用已经测试过的东西,比如 @KyleMit 链接中提到的 polyfill:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
@lmiguelmh 我自己从 Mozilla 文档本身发布了一个链接的“polyfill”怎么样?无论如何,这个功能非常简单,我真的不太关心测试。任何人都不应该使用“已经测试过”的功能,而应该自己测试他们使用的任何东西。所以你的评论有点错位。您是否发现我的功能存在特定缺陷?
@ErikE 你说得对,这个功能已经死了,任何使用你答案的人都应该知道。我一开始没有看到您的链接,因为我正在寻找“答案”【参考方案8】:
感谢您提出问题,以及使用 Array.indexOf 方法的解决方案。
我使用此解决方案中的代码创建了一个 inList() 函数,IMO 将使书写更简单,阅读更清晰:
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
【讨论】:
Javascript 数组字面量非常简单,我不明白为什么你会拆分'Houston'.inList(['LA', 'New York', 'Houston'])
。也许if (!String.prototype.inList) String.prototype.inList = function(arr) return arr.indexOf(this) >= 0;
或使用您的while
方法。【参考方案9】:
我很惊讶没有人提到一个接受字符串和列表的简单函数。
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));
【讨论】:
Jim did exactly what you suggest,Sam,2011 年 8 月 27 日 1:29。事实上,我选择的答案几乎是一样的,只是用 this 而不是参数提供字符串。 @ErikE 对不起,正如你所说,Jims 的回答对我来说似乎很奇怪。并且您接受的答案返回int
,因为有些人可能会遇到这个问题以寻找 bool
返回。认为它可能会帮助一些人。【参考方案10】:
SLaks' answer 的简化版本也可以:
if ('abcdefghij'.indexOf(str) >= 0)
// Do something
....因为字符串本身就是一种数组。 :)
如果需要,请按照我之前的描述实现 Internet Explorer 的 indexof 函数。
【讨论】:
这仅适用于非预期的单字母字符串。【参考方案11】:我的解决方案产生如下语法:
// 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。
优点:简单、直接和自我记录。
【讨论】:
扩展对象可能有问题。 bolinfest.com/javascript/inheritance.php 在“Google 地图团队以艰难的方式学到了这一点”下,并且与浏览器实现或其他用户的代码不兼容。我仍然认为 ErikE 的答案是最好的答案,因为在创建哈希映射后,遍历数组比在哈希映射中查找键要慢:myValues[key];
其中 myValues 是一个对象,键是任何字符串或数字。【参考方案12】:
这是我的:
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
【讨论】:
坦率地说,传递数组的那个可能会更有用,它可能应该在Array
的原型上:可能类似于 Array.prototype.contains
。【参考方案13】:
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'));
还有很多其他的!
【讨论】:
使用正则表达式需要避免一堆特殊字符。它也不太清楚。我认为这不是一个好的解决方案。 @ErikE 我了解您的保留意见,因此我更新了我的答案,添加了另一个不使用 RegExp 的代码,它与 IE 兼容,非常地道且快速。 您的附加代码与我在您决定发布正则表达式解决方案 5 年前提供的答案几乎重复。感谢您的参与,同时我认为最好回滚到您之前的答案。 @ErikE 是的,你说得对,我不习惯检查问题中的答案。我同意它真的很相似。【参考方案14】:使用 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 的正则表达式(文档)。
if (fruit.match(/^(banana|lemon|mango|pineapple)$/))
【讨论】:
您是否注意到您正在复制页面上的其他答案?这不会增加任何价值。 我不明白这个问题。【参考方案15】:我的小贡献:
function fnListIndexOf(pList, pValue)
return pList.split(",").indexOf (pValue);
fnListIndexOf("1,2,3,4,5,a,b,c","a")
【讨论】:
以上是关于确定字符串是不是在 JavaScript 的列表中的主要内容,如果未能解决你的问题,请参考以下文章