在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个回答

15

在所有现代浏览器中都可用的解决方案:

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 更新contains函数以进行性能优化。感谢itinance指出。

12
使用Lodash的some函数。它简洁、准确且具有很好的跨平台支持。
接受的答案甚至都不符合要求。 要求:推荐查找JavaScript数组是否包含对象的最简洁、高效的方法。 接受的答案:
$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
> -1

我的建议:

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

注意:

$.inArray用于判断标量值是否存在于标量数组中,这个功能是可行的...

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

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

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

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

11

实现这个要求的简单方法是使用find()

如果你有以下形式的对象数组:

var users = [{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "admin"},
{id: "105", name: "user"}];

然后您可以检查对象中是否已经存在具有您的值的对象:

let data = users.find(object => object['id'] === '104');

如果数据为空,则没有管理员,否则它将返回现有的对象,例如:

{id: "104", name: "admin"}

然后,您可以通过以下代码在数组中找到该对象的索引并替换对象:

let indexToUpdate = users.indexOf(data);
let newObject = {id: "104", name: "customer"};
users[indexToUpdate] = newObject;//your new object
console.log(users);

您将获得像这样的值:

[{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "customer"},
{id: "105", name: "user"}];

10

虽然 array.indexOf(x)!=-1 是最简洁的方法(并且非IE浏览器支持这种方法已经十多年了...),但它不是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中存储哈希表,但如果稍后选择编辑数组,则会得到错误的结果。与Python等语言不同,JavaScript缺乏让您保持此状态的钩子。


10

ECMAScript 6 在 find 上有一个优雅的提案。

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

这是MDN文档

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及以下版本中使用它。

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

1
这已经成为标准:http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.find - Madbreaks

10

可以使用具有"has()"方法的Set

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


8
我认为在这里使用return proxy.has(obj)比两行if-else语句更加简洁。 - Maciej Bukowski
1
function contains(arr, obj) { return new Set(arr).has(obj); } - Gordon Bean

8

6
这个已经在一年半之前发布过了(https://dev59.com/E3VC5IYBdhLWcg3wnCj6#9849276),不需要重复。 - Shadow The Spring Wizard
4
实际上,这还没有被发布。它并不是作为一个答案被发布的,而是作为对一个答案的评论,即使那样也不够清晰明了。感谢你发布它,Mina Gabriel。 - T.CK

7

好的,你只需要优化你的代码就可以得到结果!

有很多方法可以做到更加清晰和更好的优化,但我只是想获取你的模式并使用 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;
}

1
晚些说明:这不适用于例如 contains([{ a: 1, b: 2 }], { b: 2, a: 1 }),因为字符串化的对象保持属性的顺序。 - Heretic Monkey

7

惊讶的是这个问题仍然没有最新的语法,我来贡献我的看法。

假设我们有一个对象数组 arrObj ,我们想在其中搜索 obj .

Array.prototype.indexOf ->(返回 索引或 -1)通常用于查找数组中元素的索引。如果您传递对相同对象的引用,则也可以用于搜索对象。

let obj = { name: 'Sumer', age: 36 };
let arrObj = [obj, { name: 'Kishor', age: 46 }, { name: 'Rupen', age: 26 }];


console.log(arrObj.indexOf(obj));// 0
console.log(arrObj.indexOf({ name: 'Sumer', age: 36 })); //-1

console.log([1, 3, 5, 2].indexOf(2)); //3

Array.prototype.includes -> (返回 truefalse)

console.log(arrObj.includes(obj));  //true
console.log(arrObj.includes({ name: 'Sumer', age: 36 })); //false

console.log([1, 3, 5, 2].includes(2)); //true

Array.prototype.find -> (使用回调函数,返回第一个在回调函数中返回true的值/对象)。

console.log(arrObj.find(e => e.age > 40));  //{ name: 'Kishor', age: 46 }
console.log(arrObj.find(e => e.age > 40)); //{ name: 'Kishor', age: 46 }

console.log([1, 3, 5, 2].find(e => e > 2)); //3

Array.prototype.findIndex -> (接受回调函数,返回第一个返回true的值或对象的索引)。

console.log(arrObj.findIndex(e => e.age > 40));  //1
console.log(arrObj.findIndex(e => e.age > 40)); //1

console.log([1, 3, 5, 2].findIndex(e => e > 2)); //1

由于find和findIndex接受回调函数,我们可以通过创造性地设置true条件,从数组中获取任何对象(即使我们没有引用)。


6

    function countArray(originalArray) {
     
     var compressed = [];
     // make a copy of the input array
     var copyArray = originalArray.slice(0);
     
     // first loop goes over every element
     for (var i = 0; i < originalArray.length; i++) {
     
      var count = 0; 
      // loop over every element in the copy and see if it's the same
      for (var w = 0; w < copyArray.length; w++) {
       if (originalArray[i] == copyArray[w]) {
        // increase amount of times duplicate is found
        count++;
        // sets item to undefined
        delete copyArray[w];
       }
      }
     
      if (count > 0) {
       var a = new Object();
       a.value = originalArray[i];
       a.count = count;
       compressed.push(a);
      }
     }
     
     return compressed;
    };
    
    // It should go something like this:
    
    var testArray = new Array("dog", "dog", "cat", "buffalo", "wolf", "cat", "tiger", "cat");
    var newArray = countArray(testArray);
    console.log(newArray);


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