在JavaScript数组中查找一个元素的最佳方法是什么?

836

如何确定一个对象是否在数组中是最佳方式?

这是我所知道的最佳方法:

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

console.log(include([1, 2, 3, 4], 3)); // true
console.log(include([1, 2, 3, 4], 6)); // undefined


请参见:https://dev59.com/E3VC5IYBdhLWcg3wnCj6#25765186 - dr.dimitru
5
两件事情:1)“include”作为一个不修改任何状态、只返回布尔值的函数名,实在是非常糟糕的选择。2)你需要在函数结尾前添加“return (false);”。 - Aquarelle
3
从ECMAScript 2016开始,你可以使用Array.prototype.includes函数: myArray.includes(3); // 返回true - mhd
3
在ES6中,你可以像这样使用arr.find(lamda函数),例如:[1, 2, 3, 4, 5].find(x => x == 3)。如果找到元素,则返回该元素,否则返回undefined。 - ALLSYED
arr.some(element => element === obj); 使用some是最好的方式,因为它可以在找到一个项目后立即中断循环。 - Cenk Çetinkaya
8个回答

766

从ECMAScript 2016开始,您可以使用includes()方法。

arr.includes(obj);

如果您想要支持IE或其他旧版浏览器:

function include(arr,obj) {
    return (arr.indexOf(obj) != -1);
}

编辑: 然而,这个方法不适用于IE6、7或8。最好的解决方法是如果IE版本低于9,则自己定义它:

  1. Mozilla's (ECMA-262) version:

       if (!Array.prototype.indexOf)
       {
    
            Array.prototype.indexOf = function(searchElement /*, fromIndex */)
    
         {
    
    
         "use strict";
    
         if (this === void 0 || this === null)
           throw new TypeError();
    
         var t = Object(this);
         var len = t.length >>> 0;
         if (len === 0)
           return -1;
    
         var n = 0;
         if (arguments.length > 0)
         {
           n = Number(arguments[1]);
           if (n !== n)
             n = 0;
           else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
             n = (n > 0 || -1) * Math.floor(Math.abs(n));
         }
    
         if (n >= len)
           return -1;
    
         var k = n >= 0
               ? n
               : Math.max(len - Math.abs(n), 0);
    
         for (; k < len; k++)
         {
           if (k in t && t[k] === searchElement)
             return k;
         }
         return -1;
       };
    
     }
    
  2. Daniel James's version:

     if (!Array.prototype.indexOf) {
       Array.prototype.indexOf = function (obj, fromIndex) {
         if (fromIndex == null) {
             fromIndex = 0;
         } else if (fromIndex < 0) {
             fromIndex = Math.max(0, this.length + fromIndex);
         }
         for (var i = fromIndex, j = this.length; i < j; i++) {
             if (this[i] === obj)
                 return i;
         }
         return -1;
       };
     }
    
  3. roosteronacid's version:

     Array.prototype.hasObject = (
       !Array.indexOf ? function (o)
       {
         var l = this.length + 1;
         while (l -= 1)
         {
             if (this[l - 1] === o)
             {
                 return true;
             }
         }
         return false;
       } : function (o)
       {
         return (this.indexOf(o) !== -1);
       }
     );
    

我很好奇你的 Mozilla 函数版本为什么与你链接到的网站如此不同。这是你自己修改的还是只是旧版本或其他原因? - Justin G
6
@Shenjoku在2008年9月27日15:45回答了这个问题。 - Vinko Vrsalovic
好的,这就是我的答案哈哈。我无法仅通过查看 Mozilla 网站来确定是否有旧版本,所以我不确定。虽然这并不重要,只是出于好奇。无论如何,这仍然很有帮助,所以你得到了一个赞 ;) - Justin G
但是,自从什么时候indexOf与IE兼容了呢?因为我发现它与IE兼容 https://www.w3schools.com/jsref/jsref_indexof.asp - sgelves
另一个选择是使用.find(),更多信息请参见此处:https://dev59.com/Vbvoa4cB1Zd3GeqP5ohY#62305136 - Angus
显示剩余2条评论

210

11
请注意,“inArray”这个名称是不准确的,因为它不返回布尔值,而是返回第一个找到的元素的索引。因此,如果您要检查元素是否存在,应该使用if(-1 !=$.inArray(...)) ... - johndodo
5
有帮助,但我认为这不是一个适当的答案。问题标记为“javascript”,在我看来表明需要“纯净版”。 :) - iMe

34

首先,在JavaScript中实现indexOf,适用于没有该功能的浏览器。例如,查看Erik Arvidsson的数组扩展(还有相关博客文章)。然后,您可以使用indexOf而不必担心浏览器支持。下面是他indexOf实现的稍微优化版本:

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (obj, fromIndex) {
        if (fromIndex == null) {
            fromIndex = 0;
        } else if (fromIndex < 0) {
            fromIndex = Math.max(0, this.length + fromIndex);
        }
        for (var i = fromIndex, j = this.length; i < j; i++) {
            if (this[i] === obj)
                return i;
        }
        return -1;
    };
}

已更改为存储长度,以便不必每次迭代都查找。但差异并不是很大。一个不那么通用的函数可能会更快:

var include = Array.prototype.indexOf ?
    function(arr, obj) { return arr.indexOf(obj) !== -1; } :
    function(arr, obj) {
        for(var i = -1, j = arr.length; ++i < j;)
            if(arr[i] === obj) return true;
        return false;
    };

我更喜欢使用标准函数,将这种微小的优化留给真正需要的时候。但如果您对微小优化感兴趣,我已经改编了roosterononacid在评论中提到的基准测试,用于在数组中搜索进行基准测试。虽然它们相当简单,但完整的研究将测试具有不同类型、不同长度和查找出现在不同位置的对象的数组。


您链接的代码示例在处理大数组时速度很慢。请参见我实现的hasItem()函数示例中的注释。 - cllpse
看一下这些基准测试:http://blogs.sun.com/greimer/resource/loop-test.htm。for循环很慢。但我猜测基准测试中使用的数组非常大 :) - cllpse
1
http://blogs.sun.com/greimer/resource/loop-test.html - cllpse
我同意。我也很务实。但是在优化语言的基础方面,我认为实现功能尽可能高效地提高性能是良好的设计。 - cllpse
使用 Mozilla 的 Array.indexOf polyfill:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill - user1742529

13
如果数组未排序,除了使用上述提到的indexOf之外,没有更好的方法。如果数组已排序,您可以执行二分查找,其工作方式如下:
  1. 选择数组的中间元素。
  2. 您要查找的元素是否大于您选择的元素? 如果是,则已经消除了数组的底半部分。 如果不是,则已经消除了顶半部分。
  3. 选择剩余数组的中间元素,并继续按照步骤2进行,消除剩余数组的一半。 最终,您要么会找到您的元素,要么不再有需要搜索的数组。
二分查找的运行时间与数组长度的对数成比例,因此它比查看每个单独元素的速度要快得多。

2
你可能应该提到,这种方法在大型排序数组上比小型数组更快。 - cllpse
9
为什么在较小的数组上会更慢? - vidstige
3
他的意思是它在大规模情况下具有良好的可扩展性,但对于小输入来说不一定是最快的。 - Lightness Races in Orbit
这个程序的时间复杂度是O(lg n),而不是O(n),因此更具可扩展性。 - bigpotato

10

[ ].has(obj)

假设 .indexOf() 已经实现

Object.defineProperty( Array.prototype,'has',
{
    value:function(o, flag){
    if (flag === undefined) {
        return this.indexOf(o) !== -1;
    } else {   // only for raw js object
        for(var v in this) {
            if( JSON.stringify(this[v]) === JSON.stringify(o)) return true;
        }
        return false;                       
    },
    // writable:false,
    // enumerable:false
})

不要编写Array.prototype.has=function(){...,因为这将在每个数组中添加一个可枚举的元素,并且会使js出现问题。

//use like          
[22 ,'a', {prop:'x'}].has(12) // false
["a","b"].has("a") //  true

[1,{a:1}].has({a:1},1) // true
[1,{a:1}].has({a:1}) // false

使用第二个参数(标志)强制按值而不是引用进行比较

比较原始对象

[o1].has(o2,true) // true if every level value is same

8

这取决于您的目的。如果您是为Web编程,请避免使用indexOf,因为它不受Internet Explorer 6(仍有很多人在使用)的支持,或者进行条件使用:

if (yourArray.indexOf !== undefined) result = yourArray.indexOf(target);
else result = customSlowerSearch(yourArray, target);

indexOf 可能是用原生代码编写的,因此它比您在 JavaScript 中进行的任何操作都要快(除非数组适合二分搜索/二分法)。 注意:这是一个品味问题,但我会在您的例程末尾添加 return false;,以返回一个真正的布尔值...


1
哈...我真不敢想象现在还有IE6的客户端存在... - beauXjames
难道不应该像维基百科页面一样加上“截至2008年”的字样,以便人们知道这个声明已经过时了吗? - allan.simon
3
请看我回答底部的头像(和状态)。在它上面,有“answered Sep 27 '08 at 16:28”。这被称为日期,人们在Stack Overflow上查看这些日期来对答案持怀疑态度... 话虽如此,我当地的公共图书馆仍然在他们的计算机上安装了IE6!(但幸运的是,有人安装了Chrome!) - PhiLho

6

2
奇怪的URL指向一个包含有关Javascript 1.8及更高版本信息的手册! :) - Vinko Vrsalovic
这不涵盖作者所询问的对象数组。 - 29er
@VinkoVrsalovic:URL已更改为https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array - Dan Dascalescu

5

在JavaScript中检查一个对象是否是数组的稳健方法在这里详细说明:

以下是来自xa.js框架的两个函数,我将它们附加到一个utils = {}容器中。这些函数应该可以帮助您正确地检测数组。

var utils = {};

/**
 * utils.isArray
 *
 * Best guess if object is an array.
 */
utils.isArray = function(obj) {
     // do an instanceof check first
     if (obj instanceof Array) {
         return true;
     }
     // then check for obvious falses
     if (typeof obj !== 'object') {
         return false;
     }
     if (utils.type(obj) === 'array') {
         return true;
     }
     return false;
 };

/**
 * utils.type
 *
 * Attempt to ascertain actual object type.
 */
utils.type = function(obj) {
    if (obj === null || typeof obj === 'undefined') {
        return String (obj);
    }
    return Object.prototype.toString.call(obj)
        .replace(/\[object ([a-zA-Z]+)\]/, '$1').toLowerCase();
};

如果您想检查一个对象是否在一个数组中,我建议加入以下代码:
/**
 * Adding hasOwnProperty method if needed.
 */
if (typeof Object.prototype.hasOwnProperty !== 'function') {
    Object.prototype.hasOwnProperty = function (prop) {
        var type = utils.type(this);
        type = type.charAt(0).toUpperCase() + type.substr(1);
        return this[prop] !== undefined
            && this[prop] !== window[type].prototype[prop];
    };
}

最后是这个 in_array 函数:

function in_array (needle, haystack, strict) {
    var key;

    if (strict) {
        for (key in haystack) {
            if (!haystack.hasOwnProperty[key]) continue;

            if (haystack[key] === needle) {
                return true;
            }
        }
    } else {
        for (key in haystack) {
            if (!haystack.hasOwnProperty[key]) continue;

            if (haystack[key] == needle) {
                return true;
            }
        }
    }

    return false;
}

虽然这理论上回答了问题,但最好在此处包含答案的基本部分并提供参考链接。此外,如果您将相同的仅链接答案复制/粘贴到多个非常旧的问题中,它看起来就像垃圾邮件。 - Bill the Lizard
抱歉,比尔,我真的不是想让它看起来像垃圾邮件,只是想更新一些关于这个主题的旧问题。我已经在这里编辑了帖子,包括实际答案而不是链接。 - Aaria Carter-Weir
@Bill,重新阅读问题,这根本没有回答问题。我一定犯了错误。 - Aaria Carter-Weir

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