首页 文章

如何检查数组是否包含JavaScript中的对象?

提问于
浏览
3373

找出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 在数组中查找对象的问题 .

30 回答

  • 3800

    如果您使用的是JavaScript 1.6或更高版本(Firefox 1.5或更高版本),则可以使用Array.indexOf . 否则,我认为你最终将得到类似于原始代码的东西 .

  • 6

    使用:

    Array.prototype.contains = function(x){
      var retVal = -1;
    
      // x is a primitive type
      if(["string","number"].indexOf(typeof x)>=0 ){ retVal = this.indexOf(x);}
    
      // x is a function
      else if(typeof x =="function") for(var ix in this){
        if((this[ix]+"")==(x+"")) retVal = ix;
      }
    
      //x is an object...
      else {
        var sx=JSON.stringify(x);
        for(var ix in this){
          if(typeof this[ix] =="object" && JSON.stringify(this[ix])==sx) retVal = ix;
        }
      }
    
      //Return False if -1 else number if numeric otherwise string
      return (retVal === -1)?false : ( isNaN(+retVal) ? retVal : +retVal);
    }
    

    我知道这不是最好的方法,但由于没有本地IComparable方法在对象之间进行交互,我想这就像你可以比较一个数组中的两个实体一样接近 . 此外,扩展Array对象可能不是一件明智的事情,但有时候它是可以的(如果你知道它和权衡) .

  • 3

    绝不是最好的,但我只是在发挥创意并增加了曲目 .

    不要使用它

    Object.defineProperty(Array.prototype, 'exists', {
      value: function(element, index) {
    
        var index = index || 0
    
        return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index)
      }
    })
    
    
    // Outputs 1
    console.log(['one', 'two'].exists('two'));
    
    // Outputs -1
    console.log(['one', 'two'].exists('three'));
    
    console.log(['one', 'two', 'three', 'four'].exists('four'));
    
  • 368

    ECMAScript 6有一个关于find的优雅提议 .

    find方法对数组中存在的每个元素执行一次回调函数,直到找到一个回调返回true值的元素 . 如果找到这样的元素,find会立即返回该元素的值 . 否则,find返回undefined . 仅对已分配值的数组的索引调用回调;对于已删除或从未分配过值的索引,不会调用它 .

    这是MDN documentation .

    find功能就像这样 .

    function isPrime(element, index, array) {
        var start = 2;
        while (start <= Math.sqrt(element)) {
            if (element % start++ < 1) return false;
        }
        return (element > 1);
    }
    
    console.log( [4, 6, 8, 12].find(isPrime) ); // Undefined, not found
    console.log( [4, 5, 8, 12].find(isPrime) ); // 5
    

    您可以在ECMAScript 5及更低版本中使用defining the function .

    if (!Array.prototype.find) {
      Object.defineProperty(Array.prototype, 'find', {
        enumerable: false,
        configurable: true,
        writable: true,
        value: function(predicate) {
          if (this == null) {
            throw new TypeError('Array.prototype.find called on null or undefined');
          }
          if (typeof predicate !== 'function') {
            throw new TypeError('predicate must be a function');
          }
          var list = Object(this);
          var length = list.length >>> 0;
          var thisArg = arguments[1];
          var value;
    
          for (var i = 0; i < length; i++) {
            if (i in list) {
              value = list[i];
              if (predicate.call(thisArg, value, i, list)) {
                return value;
              }
            }
          }
          return undefined;
        }
      });
    }
    
  • 11

    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
    

    与使用Strict Equality ComparisonindexOf 不同, includes 使用SameValueZero等效算法进行比较 . 这意味着您可以检测数组是否包含 NaN

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

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

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

    目前它仍然是草稿,但可以polyfilled使其适用于所有浏览器 .

  • 17

    虽然 array.indexOf(x)!=-1 是最简洁的方法(并且已经被非Internet Explorer浏览器支持了十多年......),但它不是O(1),而是O(N),这很糟糕 . 如果您的数组不会更改,您可以将数组转换为哈希表,然后执行 table[x]!==undefined===undefined

    Array.prototype.toTable = function() {
        var t = {};
        this.forEach(function(x){t[x]=true});
        return t;
    }
    

    演示:

    var toRemove = [2,4].toTable();
    [1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})
    

    (不幸的是,虽然您可以创建一个Array.prototype.contains来“冻结”一个数组并将this._cache中的哈希表存储在两行中,但如果您选择稍后编辑数组,则会产生错误的结果.JavaScript没有足够的钩子 . 让你保持这种状态,例如Python不同 . )

  • 137

    使用:

    function isInArray(array, search)
    {
        return array.indexOf(search) >= 0;
    }
    
    // Usage
    if(isInArray(my_array, "my_value"))
    {
        //...
    }
    
  • 2

    这是 Array.indexOfJavaScript 1.6 compatible实现:

    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;
          };
    }
    
  • 9

    一内胆:

    function contains(arr, x) {
        return arr.filter(function(elem) { return elem == x }).length > 0;
    }
    
  • 2

    我们使用此代码段(适用于对象,数组,字符串):

    /*
     * @function
     * @name Object.prototype.inArray
     * @description Extend Object prototype within inArray function
     *
     * @param {mix}    needle       - Search-able needle
     * @param {bool}   searchInKey  - Search needle in keys?
     *
     */
    Object.defineProperty(Object.prototype, 'inArray',{
        value: function(needle, searchInKey){
    
            var object = this;
    
            if( Object.prototype.toString.call(needle) === '[object Object]' || 
                Object.prototype.toString.call(needle) === '[object Array]'){
                needle = JSON.stringify(needle);
            }
    
            return Object.keys(object).some(function(key){
    
                var value = object[key];
    
                if( Object.prototype.toString.call(value) === '[object Object]' || 
                    Object.prototype.toString.call(value) === '[object Array]'){
                    value = JSON.stringify(value);
                }
    
                if(searchInKey){
                    if(value === needle || key === needle){
                    return true;
                    }
                }else{
                    if(value === needle){
                        return true;
                    }
                }
            });
        },
        writable: true,
        configurable: true,
        enumerable: false
    });
    

    Usage:

    var a = {one: "first", two: "second", foo: {three: "third"}};
    a.inArray("first");          //true
    a.inArray("foo");            //false
    a.inArray("foo", true);      //true - search by keys
    a.inArray({three: "third"}); //true
    
    var b = ["one", "two", "three", "four", {foo: 'val'}];
    b.inArray("one");         //true
    b.inArray('foo');         //false
    b.inArray({foo: 'val'})   //true
    b.inArray("{foo: 'val'}") //false
    
    var c = "String";
    c.inArray("S");        //true
    c.inArray("s");        //false
    c.inArray("2", true);  //true
    c.inArray("20", true); //false
    
  • 12

    目前的浏览器有Array#includes,它确实如此,is widely supported,并且对于旧浏览器有polyfill .

    > ['joe', 'jane', 'mary'].includes('jane');
    true
    

    您也可以使用Array#indexOf,它不是直接的,但不需要Polyfills用于过时的浏览器 .

    jQuery提供了$.inArray,它在功能上等同于 Array#indexOf .

    underscore.js,一个JavaScript实用程序库,提供了_.contains(list, value),别名 _.include(list, value) ,如果传递了一个JavaScript数组,它们都在内部使用indexOf .

    其他一些框架提供了类似的方法:

    请注意,某些框架将此实现为函数,而其他框架则将该函数添加到数组原型中 .

  • 7

    我使用以下内容:

    Array.prototype.contains = function (v) {
        return this.indexOf(v) > -1;
    }
    
    var a = [ 'foo', 'bar' ];
    
    a.contains('foo'); // true
    a.contains('fox'); // false
    
  • 12

    使用lodash的some函数 .

    它简洁,准确,并具有很好的跨平台支持 .

    接受的答案甚至不符合要求 .

    要求:推荐最简洁有效的方法来确定JavaScript数组是否包含对象 .

    Accepted Answer:

    $.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
    > -1
    

    My recommendation:

    _.some([{'a': 1}, {'b': 2}], {'b': 2})
    > true
    

    笔记:

    $ .inArray可以很好地确定标量数组中是否存在标量值...

    $.inArray(2, [1,2])
    > 1
    

    ...但问题明确要求确定对象是否包含在数组中的有效方法 .

    为了处理标量和对象,您可以这样做:

    (_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)
    
  • 3

    好的,你可以只用 optimise your 代码来获得结果!有很多方法可以做到更清洁,更好,但我只是想得到你的模式并使用 JSON.stringify 适用于它,只需在你的情况下做这样的事情:

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

    最顶层的答案假设原始类型,但如果你想知道一个数组是否包含一个具有某些特征的对象,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 support table

  • 54
    function contains(a, obj) {
        return a.some(function(element){return element == obj;})
    }
    

    Array.prototype.some()已添加到第5版的ECMA-262标准中

  • 166

    这是Prototype does it

    /**
     *  Array#indexOf(item[, offset = 0]) -> Number
     *  - item (?): A value that may or may not be in the array.
     *  - offset (Number): The number of initial items to skip before beginning the
     *      search.
     *
     *  Returns the position of the first occurrence of `item` within the array &mdash; or
     *  `-1` if `item` doesn't exist in the array.
    **/
    function indexOf(item, i) {
      i || (i = 0);
      var length = this.length;
      if (i < 0) i = length + i;
      for (; i < length; i++)
        if (this[i] === item) return i;
      return -1;
    }
    

    另请参阅here了解它们是如何连接它的 .

  • 99

    可以使用Set,其方法为"has()":

    function contains(arr, obj) {
      var proxy = new Set(arr);
      if (proxy.has(obj))
        return true;
      else
        return false;
    }
    
    var arr = ['Happy', 'New', 'Year'];
    console.log(contains(arr, 'Happy'));
    
  • 69

    开箱即用,如果您多次进行此调用,使用关联数组Map使用散列函数进行查找会更加高效 .

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

  • 50

    A hopefully faster bidirectional indexOf / lastIndexOf alternative

    2015

    虽然新方法includes非常好,但现在支持基本上为零 .

    我一直想着替换慢的indexOf / lastIndexOf函数 .

    已找到一种高效的方式,查看最佳答案 . 从那些我选择@Damir Zekic发布的 contains 函数来看,这应该是最快的 . 但它也指出基准是从2008年开始的,因此已经过时了 .

    我也更喜欢 while 而不是 for ,但由于没有特殊原因,我结束了用for循环编写函数 . 它也可以用 while -- 来完成 .

    如果我在做这个时检查数组的两侧,迭代速度要慢得多,我很好奇 . 显然不是,因此这个功能比最高投票的功能快两倍左右 . 显然它也比原生的快 . 这在现实环境中,您永远不知道您正在搜索的值是在数组的开头还是结尾 .

    当你知道你刚推送一个带有值的数组时,使用lastIndexOf仍然可能是最好的解决方案,但是如果你必须通过大数组并且结果可能无处不在,那么这可能是一个可以让事情变得更快的可靠解决方案 .

    Bidirectional indexOf/lastIndexOf

    function bidirectionalIndexOf(a, b, c, d, e){
      for(c=a.length,d=c*1; c--; ){
        if(a[c]==b) return c; //or this[c]===b
        if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
      }
      return -1
    }
    
    //Usage
    bidirectionalIndexOf(array,'value');
    

    性能测试

    http://jsperf.com/bidirectionalindexof

    作为测试,我创建了一个包含100k条目的数组 .

    三个查询:在数组的开头,中间和末尾 .

    我希望你也发现这很有趣并测试性能 .

    注意:正如您所看到的,我略微修改了 contains 函数以反映indexOf和lastIndexOf输出(所以基本上 trueindexfalse-1 ) . 这不应该伤害它 .

    数组原型变体

    Object.defineProperty(Array.prototype,'bidirectionalIndexOf',{value:function(b,c,d,e){
      for(c=this.length,d=c*1; c--; ){
        if(this[c]==b) return c; //or this[c]===b
        if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
      }
      return -1
    },writable:false, enumerable:false});
    
    // Usage
    array.bidirectionalIndexOf('value');
    

    该函数也可以很容易地修改为返回true或false,甚至是对象,字符串或其他任何东西 .

    这是 while 变体:

    function bidirectionalIndexOf(a, b, c, d){
      c=a.length; d=c-1;
      while(c--){
        if(b===a[c]) return c;
        if(b===a[d-c]) return d-c;
      }
      return c
    }
    
    // Usage
    bidirectionalIndexOf(array,'value');
    

    这怎么可能?

    我认为在数组中获取反射索引的简单计算非常简单,它比实际循环迭代快两倍 .

    下面是一个复杂的示例,每次迭代执行三次检查,但这只能通过更长的计算才能实现,这会导致代码速度变慢 .

    http://jsperf.com/bidirectionalindexof/2

  • 22

    b 是值, a 是数组 . 它返回 truefalse

    function(a, b) {
        return a.indexOf(b) != -1
    }
    
  • 42

    Update: As @orip mentions in comments, the linked benchmark was done in 2008, so results may not be relevant for modern browsers. However, you probably need this to support non-modern browsers anyway and they probably haven't been updated since. Always test for yourself.

    正如其他人所说,通过数组的迭代可能是最好的方法,但是has been proven减少 while 循环是在JavaScript中迭代的最快方式 . 因此,您可能需要重写代码,如下所示:

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

    当然,您也可以扩展Array原型:

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

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

    如果您不需要担心与页面上运行的其他JavaScript的兼容性,那就去吧,否则,我会推荐更笨拙但更安全的独立功能解决方案 .

  • 14

    你也可以使用这个技巧:

    var arrayContains = function(object) {
      return (serverList.filter(function(currentObject) {
        if (currentObject === object) {
          return currentObject
        }
        else {
          return false;
        }
      }).length > 0) ? true : false
    }
    
  • 8
    function inArray(elem,array)
    {
        var len = array.length;
        for(var i = 0 ; i < len;i++)
        {
            if(array[i] == elem){return i;}
        }
        return -1;
    }
    

    如果找到则返回数组索引,如果未找到则返回-1

  • 7

    适用于所有现代浏览器的解决方案:

    function contains(arr, obj) {
      const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration
      return arr.some(item => JSON.stringify(item) === stringifiedObj);
    }
    

    用法:

    contains([{a: 1}, {a: 2}], {a: 1}); // true
    

    IE6解决方案:

    function contains(arr, obj) {
      var stringifiedObj = JSON.stringify(obj)
      return arr.some(function (item) {
        return JSON.stringify(item) === stringifiedObj;
      });
    }
    
    // .some polyfill, not needed for IE9+
    if (!('some' in Array.prototype)) {
      Array.prototype.some = function (tester, that /*opt*/) {
        for (var i = 0, n = this.length; i < n; i++) {
          if (i in this && tester.call(that, this[i], i, this)) return true;
        } return false;
      };
    }
    

    用法:

    contains([{a: 1}, {a: 2}], {a: 1}); // true
    

    为什么要使用JSON.stringify?

    Array.indexOfArray.includes (以及此处的大部分答案)仅按参考而非按值进行比较 .

    [{a: 1}, {a: 2}].includes({a: 1});
    // false, because {a: 1} is a new object
    

    奖金

    非优化的ES6单线程:

    [{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
    // true
    

    注意:如果按键的顺序相同,则按值比较对象会更好,所以为了安全起见,您可以先使用类似这样的包对键进行排序:https://www.npmjs.com/package/sort-keys


    使用perf优化更新了 contains 函数 . 谢谢itinance指出来了 .

  • 3

    indexOf也许,但这是"JavaScript extension to the ECMA-262 standard; as such it may not be present in other implementations of the standard."

    例:

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

    AFAICS Microsoft does not offer some kind of alternative对此,但是如果您愿意,可以在Internet Explorer(以及其他不支持 indexOf 的浏览器)中为数组添加类似的功能,例如quick Google search reveals(例如,this one) .

  • 3

    正如其他人提到的那样,你可以使用 Array.indexOf ,但它不是https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf的代码来使它在旧浏览器中的工作方式相同 .

    indexOf是ECMA-262标准的最新成员;因此它可能不存在在所有浏览器中 . 您可以通过在脚本的开头插入以下代码来解决此问题,允许在本身不支持它的实现中使用indexOf . 该算法正好是ECMA-262第5版中指定的算法,假设Object,TypeError,Number,Math.floor,Math.abs和Math.max具有其原始值 .

    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
            "use strict";
            if (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 > 1) {
                n = Number(arguments[1]);
                if (n != n) { // shortcut for verifying if it's NaN
                    n = 0;
                } else if (n != 0 && n != Infinity && n != -Infinity) {
                    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;
        }
    }
    
  • 3

    使用:

    var myArray = ['yellow', 'orange', 'red'] ;
    
    alert(!!~myArray.indexOf('red')); //true
    

    Demo

    要确切知道 tilde ~ 此时做了什么,请参阅此问题What does a tilde do when it precedes an expression? .

  • 26

    如果您反复检查数组中是否存在对象,您应该查看

    • 通过在数组中执行insertion sort来始终对数组进行排序(将新对象放在正确的位置)

    • 将对象更新为删除已排序的插入操作和

    • contains(a, obj) 中使用binary search查找 .

相关问题