在JavaScript中如何检查数组是否包含某个值?

4890

什么是在JavaScript中查找数组是否包含特定值的最简洁和高效的方法?

以下是我所了解的唯一方法:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (a[i] === obj) {
            return true;
        }
    }
    return false;
}

有没有更好、更简洁的方法来完成这个任务?

这与Stack Overflow问题 Best way to find an item in a JavaScript Array?非常相关,该问题讨论了使用indexOf在数组中查找对象的方法。


64
刚刚测试了一下,你的方法实际上是跨浏览器最快的方式:http://jsperf.com/find-element-in-obj-vs-array/2(除了在变量中预先保存a.length之外)。而使用indexOf(如$.inArray中所示)则慢得多。 - Jörn Berkefeld
23
许多人回复说 Array#indexOf 是在这里最好的选择。但是,如果你想要一些可以正确转换为布尔值的东西,请使用以下代码:~[1,2,3].indexOf(4) 将返回 0,被评估为 false,而 ~[1,2,3].indexOf(3) 将返回 -3,被评估为 true。 - lordvlad
13
使用~不能将值转换为布尔型,你需要使用!。但在本例中,你需要检查是否等于-1,所以函数可能会结束返回[1,2,3].indexOf(3) === -1。~是二进制非运算符,它会独立地翻转每个位的值。 - mcfedr
19
[1,2,3].indexOf(4)实际上会返回-1。正如@mcfedr指出的,~是比特非运算符,参见ES5 11.4.8。问题在于,由于-1的二进制表示只包含1,其补码为0,这会被评估为false。任何其他数字的补码都不为零,因此为true。所以,~可以很好地与indexOf一起使用。 - mknecht
7
标题有误导性。[[1,2],[3,4]].includes([3,4])在哪里? - mplungjan
显示剩余4条评论
62个回答

5386

现代浏览器都支持Array#includes,它可以精确地实现这个功能,并且被除IE以外的所有人广泛支持

console.log(['joe', 'jane', 'mary'].includes('jane')); // true

您还可以使用Array#indexOf,这种方法不太直接,但不需要为过时的浏览器提供polyfills。

console.log(['joe', 'jane', 'mary'].indexOf('jane') >= 0); // true


许多框架也提供类似的方法: 请注意,一些框架将此实现为一个函数,而其他框架则将该函数添加到数组原型中。

45
MooTools还有一个Array.contains方法,返回一个布尔值,这似乎是真正的问题。 - Ryan Florence
23
原型还具有Array.include,它返回一个布尔值。 - user102008
48
如果你正在使用一款好的浏览器,你可以直接使用array.indexOf(object) != -1 - Sam Soffes
14
不要单独使用indexOf作为条件,因为第一个元素将返回0并被评估为假值。 - plus-
265
inArray 是一个返回元素索引,如果不存在则返回-1的函数名称非常糟糕。我会期望返回一个布尔值。 - Tim
显示剩余3条评论

502

2019年更新:本答案发布于2008年(已有11年历史!),与现代JS用法不相关。承诺的性能提升是基于当时浏览器进行的基准测试,可能与现代JS执行上下文无关。如果您需要简单的解决方案,请查找其他答案。如果您需要最佳性能,请在相关执行环境中进行基准测试。

正如其他人所说,通过数组迭代可能是最好的方法,但已经证明在JavaScript中使用递减的while循环是最快的迭代方式。因此,您可能需要按照以下方式重写代码:

function contains(a, obj) {
    var i = a.length;
    while (i--) {
       if (a[i] === obj) {
           return true;
       }
    }
    return false;
}

当然,您也可以扩展数组原型:
Array.prototype.contains = function(obj) {
    var i = this.length;
    while (i--) {
        if (this[i] === obj) {
            return true;
        }
    }
    return false;
}

现在,您可以简单地使用以下内容:

alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false

29
但是要小心:https://dev59.com/E3VC5IYBdhLWcg3wnCj6#1342312 - Tyler
29
"Proven" 是一个强有力的词语。JS 引擎不断改进,3年前测量的执行时间已经非常过时了。 - orip

250

顶级答案假设原始类型,但如果您想找出数组是否包含具有某些特征的对象,则Array.prototype.some()是一种优雅的解决方案:

const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]

items.some(item => item.a === '3')  // returns true
items.some(item => item.a === '4')  // returns false

它的好处在于一旦找到元素,迭代就会中止,因此可以节省不必要的迭代循环。

此外,由于它返回布尔值,因此它非常适合放在 if 语句中使用:

if (items.some(item => item.a === '3')) {
  // do something
}

*正如评论中Jamess所指出的,在回答时,即2018年9月,Array.prototype.some()得到了全面支持:caniuse.com 支持表格


我正在使用Typescript,我的数组包含枚举类型的项。 "Includes" 对我不起作用,这个解决方案有效。欢迎提供任何其他适用于Typescript的解决方案。 - Amir Mehrnam

248

indexOf可能被使用,但它是“ECMA-262标准的JavaScript扩展;因此,在标准的其他实现中可能不存在。”

示例:

[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1

据我所知,微软没有提供任何替代方法。但是,如果您愿意,可以在Internet Explorer(和其他不支持indexOf的浏览器)中为数组添加类似的功能,快速谷歌搜索揭示了一些解决方案(例如,这个)。


210

ECMAScript 7引入了Array.prototype.includes

使用方法如下:

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false

它还接受一个可选的第二个参数fromIndex

[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true

indexOf 方法使用 严格相等比较算法,而 includes 方法使用 SameValueZero 相等比较算法。这意味着你可以通过 includes 方法检测数组中是否包含 NaN

[1, 2, NaN].includes(NaN); // true

indexOf不同,includes不会跳过缺失的索引:

new Array(5).includes(undefined); // true

它可以通过多填充来使其在所有浏览器上工作。


125

假设你已经像下面这样定义了一个数组:

const array = [1, 2, 3, 4]

以下有三种检查是否存在3的方法。它们都返回truefalse

本地数组方法(自ES2016以来)(兼容性表格)

array.includes(3) // true

作为自定义的数组方法(ES2016之前)

// Prefixing the method with '_' to avoid name clashes
Object.defineProperty(Array.prototype, '_includes', { value: function (v) { return this.indexOf(v) !== -1 }})
array._includes(3) // true

简单函数

const includes = (a, v) => a.indexOf(v) !== -1
includes(array, 3) // true

64
"" 是一个操作符,它对一个数字执行向下取整、取反并减去1的操作。如果 indexOf 操作失败,它会返回 -1,因此 "" 将 -1 转换为 "0"。使用 "!!" 可以将数字转换成布尔值 (例如 !!0 等于 false)。 - william malo

87

这里是一个兼容 JavaScript 1.6Array.indexOf实现:

if (!Array.indexOf) {
    Array.indexOf = [].indexOf ?
        function(arr, obj, from) {
            return arr.indexOf(obj, from);
        } :
        function(arr, obj, from) { // (for IE6)
            var l = arr.length,
                i = from ? parseInt((1 * from) + (from < 0 ? l : 0), 10) : 0;
            i = i < 0 ? 0 : i;
            for (; i < l; i++) {
                if (i in arr && arr[i] === obj) {
                    return i;
                }
            }
            return -1;
        };
}

这看起来很不错,但有点困惑:
  • 第1行和第3行的测试不是等价的吗?
  • 如果必要的话,测试原型并将函数添加到Array.prototype是否更好?
- Avi Flax
12
它们并不相等。[].indexOfArray.prototype.indexOf 的简写。 我们这些偏执防御性的 JavaScript 程序员尽可能避免扩展原生对象的原型。 - Már Örlygsson
2
“[].indexOf” 是创建一个新数组,然后访问“indexOf”,而“Array.prototype.indexOf”直接访问原型。 - alex
4
@alex 是的,[].indexOf === Array.prototype.indexOf(在 FireBug 中试一下),但是相反地,[].indexOf !== Array.indexOf - Már Örlygsson

62

使用:

function isInArray(array, search)
{
    return array.indexOf(search) >= 0;
}

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}

27
x ? true : false通常是多余的。在这里也是如此。 - Ry-
1
@minitech 为什么你说它是多余的? - Matías Cánepa
11
array.indexOf(search) >= 0 已经是一个布尔值。只需 return array.indexOf(search) >= 0 - Ry-

58

扩展JavaScript Array对象是一个非常糟糕的想法,因为您会在for-in循环中引入新属性(自定义方法),这可能会破坏现有脚本。几年前,Prototype库的作者不得不重新设计他们的库实现,以删除这种情况。

如果您不需要担心与页面上运行的其他JavaScript的兼容性,请尽管去做,否则,我建议使用更麻烦但更安全的独立函数解决方案。


25
我不同意。正是出于这个原因,for-in循环不应该用于数组。使用for-in循环会在使用流行的JS库之一时出现问题。 - Tomas

45

性能

今天,即2020年1月7日,我在MacOS High Sierra 10.13.6上使用Chrome v78.0.0、Safari v13.0.4和Firefox v71.0.0对15个选择的解决方案进行了测试。以下是结论:

  • 基于JSON, Set和出人意料的find (K,N,O)的解决方案在所有浏览器上速度最慢
  • es6中的includes (F)只在Chrome上速度快
  • 基于for (C,D)和indexOf (G,H)的解决方案在小数组和大数组上都非常快,因此它们可能是效率最高的解决方案
  • 循环时索引递减的解决方案(B)较慢,可能是因为CPU缓存工作方式的原因。
  • 我还进行了一个大数组的测试,在其中搜索元素位于数组长度的66%位置,基于for (C,D,E)的解决方案给出了类似的结果(约630次操作/秒,但E在Safari和Firefox上比C和D慢10-20%)

结果

enter image description here

细节

我进行了两个测试用例:一个包含10个元素的数组,另一个包含1000000个元素的数组。在两种情况下,我们都将搜索的元素放在数组的中间。

let log = (name,f) => console.log(`${name}: 3-${f(arr,'s10')}  's7'-${f(arr,'s7')}  6-${f(arr,6)} 's3'-${f(arr,'s3')}`)

let arr = [1,2,3,4,5,'s6','s7','s8','s9','s10'];
//arr = new Array(1000000).fill(123); arr[500000]=7;

function A(a, val) {
    var i = -1;
    var n = a.length;
    while (i++<n) {
       if (a[i] === val) {
           return true;
       }
    }
    return false;
}

function B(a, val) {
    var i = a.length;
    while (i--) {
       if (a[i] === val) {
           return true;
       }
    }
    return false;
}

function C(a, val) {
    for (var i = 0; i < a.length; i++) {
        if (a[i] === val) return true;
    }
    return false;
}

function D(a,val)
{
    var len = a.length;
    for(var i = 0 ; i < len;i++)
    {
        if(a[i] === val) return true;
    }
    return false;
} 

function E(a, val){  
  var n = a.length-1;
  var t = n/2;
  for (var i = 0; i <= t; i++) {
        if (a[i] === val || a[n-i] === val) return true;
  }
  return false;
}

function F(a,val) {
 return a.includes(val);
}

function G(a,val) {
 return a.indexOf(val)>=0;
}

function H(a,val) {
 return !!~a.indexOf(val);
}

function I(a, val) {
  return a.findIndex(x=> x==val)>=0;
}

function J(a,val) {
 return a.some(x=> x===val);
}

function K(a, val) {
  const s = JSON.stringify(val);
  return a.some(x => JSON.stringify(x) === s);
}

function L(a,val) {
 return !a.every(x=> x!==val);
}

function M(a, val) {
  return !!a.find(x=> x==val);
}

function N(a,val) {
 return a.filter(x=>x===val).length > 0;
}

function O(a, val) {
  return new Set(a).has(val);
}

log('A',A);
log('B',B);
log('C',C);
log('D',D);
log('E',E);
log('F',F);
log('G',G);
log('H',H);
log('I',I);
log('J',J);
log('K',K);
log('L',L);
log('M',M);
log('N',N);
log('O',O);
This shippet only presents functions used in performance tests - it not perform tests itself!

小数组 - 10 个元素

您可以在您的计算机上进行测试这里

enter image description here

大数组 - 1,000,000 个元素

您可以在您的计算机上进行测试这里

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接