Как проверить, содержит ли массив объект в JavaScript? - PullRequest
3588 голосов
/ 26 октября 2008

Какой самый краткий и эффективный способ узнать, содержит ли массив JavaScript объект?

Это единственный способ, которым я знаю:

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

Есть ли лучший и более краткий способ сделать это?

Это очень тесно связано с вопросом переполнения стека Лучший способ найти элемент в массиве JavaScript? , который обращается к поиску объектов в массиве с использованием indexOf.

Ответы [ 46 ]

24 голосов
/ 07 января 2015

One-вкладыш:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}
22 голосов
/ 15 июня 2014

Я использую следующее:

Array.prototype.contains = function (v) {
    return this.indexOf(v) > -1;
}

var a = [ 'foo', 'bar' ];

a.contains('foo'); // true
a.contains('fox'); // false
19 голосов
/ 12 сентября 2014
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some () был добавлен к стандарту ECMA-262 в 5-м издании

14 голосов
/ 19 мая 2015

Надеемся, что более быстрый двунаправленный indexOf / lastIndexOf альтернатива

2015

В то время как новый метод включает очень хорош, поддержка в настоящее время в основном равна нулю.

Давно я думал о том, как заменить медленные функции indexOf / lastIndexOf.

Эффективный путь уже найден, глядя на топовые ответы. Я выбрал функцию contains от @Damir Zekic, которая должна быть самой быстрой. Но в нем также говорится, что контрольные показатели взяты с 2008 года и поэтому устарели.

Я также предпочитаю while вместо for, но по непонятной причине я закончил написание функции с помощью цикла for. Это также можно сделать с помощью while --.

Мне было бы любопытно, если итерация была бы намного медленнее, если я проверяю обе стороны массива, делая это. По-видимому, нет, и поэтому эта функция примерно в два раза быстрее, чем те, которые проголосовали сверху. Очевидно, что это также быстрее, чем родной. Это в реальной среде, где вы никогда не узнаете, находится ли искомое значение в начале или в конце массива.

Когда вы знаете, что вы только что выдвинули массив со значением, использование lastIndexOf остается, вероятно, лучшим решением, но если вам нужно перемещаться по большим массивам, и результат может быть везде, это может быть надежным решением для ускорения работы.

Двунаправленный индексOf / 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

В качестве теста я создал массив с 100 тыс. Записей.

Три запроса: в начале, в середине и в конце массива.

Надеюсь, вы тоже найдете это интересным и протестируете производительность.

Примечание: Как вы можете видеть, я немного изменил функцию contains, чтобы отразить выходные данные indexOf & lastIndexOf (поэтому в основном true с index и false с -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');

Функцию также можно легко изменить, чтобы она возвращала истину или ложь или даже объект, строку или что-то еще.

А вот вариант 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

12 голосов
/ 10 сентября 2014

Мы используем этот фрагмент (работает с объектами, массивами, строками):

/*
 * @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
});

Использование:

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 голосов
/ 27 июня 2012
function inArray(elem,array)
{
    var len = array.length;
    for(var i = 0 ; i < len;i++)
    {
        if(array[i] == elem){return i;}
    }
    return -1;
} 

Возвращает индекс массива, если найден, или -1, если не найден

12 голосов
/ 05 февраля 2011

Если вы неоднократно проверяете наличие объекта в массиве, вам, возможно, стоит взглянуть на

  1. Сохранение массива всегда отсортированным путем сортировки вставок в вашем массиве (поместите новые объекты в нужное место)
  2. Сделать обновление объектов как удаление + сортировка операции вставки и
  3. Используйте бинарный поиск lookup в вашем contains(a, obj).
12 голосов
/ 26 октября 2008

Если вы используете JavaScript 1.6 или новее (Firefox 1.5 или новее), вы можете использовать Array.indexOf . В противном случае, я думаю, вы получите что-то похожее на ваш оригинальный код.

9 голосов
/ 25 января 2017

Решение, которое работает во всех современных браузерах:

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.indexOf и Array.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 за указание на это.

9 голосов
/ 21 октября 2015

Используйте функцию Лодаша 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...