Как проверить, содержит ли массив объект в JavaScript?

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

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

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

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

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

3368
задан 26 окт. '08 в 1:14
источник поделиться
43 ответов
  • 1
  • 2

В настоящее время браузеры содержат Array#includes, который делает именно это, широко поддерживается и имеет полиполк для старых браузеров.

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

Вы также можете использовать Array#indexOf, который является менее прямым, но не требует Polyfills для устаревших браузеров.

jQuery предлагает $.inArray, который функционально эквивалентен Array#indexOf.

underscore.js, библиотека утилиты JavaScript, предлагает _.contains(list, value), псевдоним _.include(list, value), оба из которых используют indexOf внутри, если передают массив JavaScript.

Некоторые другие структуры предлагают аналогичные методы:

Обратите внимание, что некоторые структуры реализуют это как функцию, а другие добавляют функцию к прототипу массива.

3798
ответ дан 24 сент. '09 в 22:35
источник

Обновление. Поскольку @orip упоминает в комментариях, связанный бенчмарк был выполнен в 2008 году, поэтому результаты могут не иметь отношения к современным браузерам. Однако вам, вероятно, понадобится это, чтобы поддерживать несовременные браузеры в любом случае, и, вероятно, они не обновлялись с тех пор. Всегда проверяйте себя.

Как говорили другие, итерация через массив, вероятно, является лучшим способом, но доказано, что уменьшающийся цикл 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
368
ответ дан 26 окт. '08 в 2:10
источник

indexOf, но это "расширение JavaScript для стандарта ECMA-262, поэтому оно может отсутствовать в других реализациях стандарта".

Пример:

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

AFAICS Microsoft не предлагает какой-то альтернативы, но вы можете добавить аналогичную функциональность для массивов в Internet Explorer (и других браузерах, которые не поддерживайте indexOf), если вы хотите, поскольку быстрый поиск Google показывает (например, этот.

165
ответ дан 26 окт. '08 в 1:49
источник

В 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, который использует Strict Equality Comparison, includes сравнивается с использованием SameValueZero. Это означает, что вы можете определить, содержит ли массив NaN:

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

В отличие от indexOf, includes не пропускает отсутствующие индексы:

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

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

137
ответ дан 01 янв. '15 в 4:40
источник

b - это значение, а a - массив. Он возвращает true или false:

function(a, b) {
    return a.indexOf(b) != -1
}
99
ответ дан 24 марта '12 в 7:59
источник

Здесь поддержка JavaScript 1.620 > :

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;
      };
}
69
ответ дан 27 окт. '08 в 3:38
источник

Верхние ответы предполагают примитивные типы, но если вы хотите узнать, содержит ли массив объект с некоторой чертой, 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 года, Array.prototype.some() полностью поддерживается: таблица поддержки caniuse.com

52
ответ дан 18 июля '14 в 17:36
источник

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

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

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}
50
ответ дан 13 сент. '13 в 20:32
источник

Расширение JavaScript Array - это действительно плохая идея, потому что вы вводите новые свойства (ваши собственные методы) в циклы for-in, которые могут нарушать существующие скрипты. Несколько лет назад авторам библиотеки Prototype пришлось перестроить свою библиотечную реализацию, чтобы удалить именно такие вещи.

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

42
ответ дан 27 авг. '09 в 19:45
источник

Размышляя о коробке на секунду, если вы делаете этот вызов много раз, гораздо эффективнее использовать ассоциативный массив, который будет выполнять поиск, используя хеш-функцию.

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

26
ответ дан 23 дек. '09 в 18:59
источник

Однострочник:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}
23
ответ дан 07 янв. '15 в 15:49
источник

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

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

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

a.contains('foo'); // true
a.contains('fox'); // false
22
ответ дан 15 июня '14 в 4:15
источник
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

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

17
ответ дан 12 сент. '14 в 19:55
источник

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

2015

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

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

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

Я также предпочитаю while over 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');

Функция также может быть легко изменена, чтобы возвращать 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

14
ответ дан 19 мая '15 в 23:23
источник

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

  • Сохранение массива, отсортированного во все времена, делая insertion sort в вашем массиве (помещаем новые объекты в нужное место)
  • Сделать обновления объектов в качестве операции удаления + сортировки вставки и
  • Используйте бинарный поиск в вашем contains(a, obj).
12
ответ дан 05 февр. '11 в 21:02
источник
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
ответ дан 27 июня '12 в 15:32
источник

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

/*
 * @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
11
ответ дан 10 сент. '14 в 15:12
источник

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

10
ответ дан 26 окт. '08 в 1:44
источник

Используйте 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)
9
ответ дан 21 окт. '15 в 13:57
источник

В то время как array.indexOf(x)!=-1 является наиболее кратким способом сделать это (и был поддержан браузерами не интернет-браузеров более десятилетия...), это не 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, например.)

8
ответ дан 22 апр. '12 в 8:17
источник

ECMAScript 6 имеет элегантное предложение по поиску.

Метод find выполняет функцию обратного вызова один раз для каждого элемента присутствует в массиве, пока не найдет тот, где обратный вызов возвращает истинное значение стоимость. Если такой элемент найден, find немедленно возвращает значение этого элемента. В противном случае найдите return undefined. обратный вызов вызывается только для индексов массива, которым присвоены значения; Это не вызывается для индексов, которые были удалены или которые никогда не были были присвоены значения.

Ниже приведена документация MDN.

Функция поиска работает следующим образом.

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;
    }
  });
}
7
ответ дан 29 мая '14 в 19:55
источник

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

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

Bonus

Не оптимизированный однострочный ES6:

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

Примечание: Сравнение объектов по значению будет работать лучше, если ключи находятся в одном порядке, поэтому, чтобы быть в безопасности, вы можете сначала сортировать ключи с помощью такого пакета: https://www.npmjs.com/package/sort-keys


Обновлена ​​функция contains с первичной оптимизацией. Спасибо itinance за то, что указали это.

7
ответ дан 25 янв. '17 в 3:22
источник

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

var myArray = ['yellow', 'orange', 'red'] ;

alert(!!~myArray.indexOf('red')); //true

Демо

Чтобы точно знать, что делает tilde ~ в этой точке, обратитесь к этому вопросу Что делает тильда, когда она предшествует выражению?.

6
ответ дан 06 окт. '13 в 15:25
источник

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

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

Я знаю, что это не лучший способ пойти, но поскольку нет никакого естественного способного для взаимодействия между объектами, я полагаю, что это так близко, что вы можете сравнить два объекта в массиве. Кроме того, расширение объекта Array может быть неразумным, но иногда это нормально (если вы знаете об этом и компромисс).

3
ответ дан 06 янв. '12 в 16:43
источник

Здесь Прототип делает это:

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

Также см. здесь для их подключения.

3
ответ дан 27 авг. '09 в 20:10
источник

ОК, вы можете просто оптимизировать свой код, чтобы получить результат! Есть много способов сделать это, которые чище и лучше, но я просто хотел получить ваш шаблон и применить к нему с помощью 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;
}
3
ответ дан 02 июля '17 в 14:31
источник

Можно использовать 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'));
3
ответ дан 16 нояб. '15 в 15:26
источник

Вы также можете использовать этот трюк:

var arrayContains = function(object) {
  return (serverList.filter(function(currentObject) {
    if (currentObject === object) {
      return currentObject
    }
    else {
      return false;
    }
  }).length > 0) ? true : false
}
3
ответ дан 10 янв. '16 в 13:06
источник

Отнюдь не лучший, но я просто стал творческим и добавлял в репертуар.

Не используйте этот

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'));
2
ответ дан 09 янв. '16 в 9:38
источник
  • Либо используйте Array.indexOf(Object).
  • С ECMA 7 можно использовать массив Array.includes(Object).
  • С ECMA 6 вы можете использовать Array.find(FunctionName), где FunctionName является пользователем определенную функцию для поиска объекта в массиве.

    Надеюсь, что это поможет!

2
ответ дан 18 авг. '17 в 7:20
источник
  • 1
  • 2

Другие вопросы по меткам или Задайте вопрос