Как проверить, содержит ли массив объект в JavaScript?
Какой самый краткий и эффективный способ выяснить, содержит ли массив JavaScript объект?
Это единственный способ, которым я знаю, чтобы сделать это:
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
Есть ли лучший и более краткий способ сделать это?
Это очень тесно связано с вопросом переполнения стека. Лучший способ найти элемент в массиве JavaScript? который обращается к поиску объектов в массиве, используя indexOf
,
63 ответа
Текущие браузеры имеют Array#includes
, который делает именно это, широко поддерживается и имеет полифил для старых браузеров.
> ['joe', 'jane', 'mary'].includes('jane');
true
Вы также можете использовать Array#indexOf
, который является менее прямым, но не требует Polyfills для устаревших браузеров.
JQuery предлагает $.inArray
, который функционально эквивалентен Array#indexOf
,
http://underscorejs.org/, библиотека утилит JavaScript, предлагает _.contains(list, value)
псевдоним _.include(list, value)
Оба они используют indexOf внутренне, если передают массив JavaScript.
Некоторые другие платформы предлагают аналогичные методы:
- Инструментарий Dojo:
dojo.indexOf(array, value, [fromIndex, findLast])
- Прототип:
array.indexOf(value)
- MooTools:
array.indexOf(value)
- MochiKit:
findValue(array, value)
- MS Ajax:
array.indexOf(value)
- Ext:
Ext.Array.contains(array, value)
- Lodash:
_.includes(array, value, [from])
(является_.contains
предыдущая 4.0.0) - ECMAScript 2016:
array.includes(value)
Обратите внимание, что некоторые фреймворки реализуют это как функцию, в то время как другие добавляют функцию в прототип массива.
Обновление: как @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
Верхние ответы предполагают примитивные типы, но если вы хотите выяснить, содержит ли массив объект с некоторой чертой, 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
}
* Как отметил Джеймс в комментарии, по состоянию на сегодня, сентябрь 2018 года, Array.prototype.some()
полностью поддерживается: таблица поддержки caniuse.com
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 (например, этот).
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
, который использует сравнение строгого равенства, includes
сравнивает используя алгоритм равенства SameValueZero. Это означает, что вы можете определить, содержит ли массив NaN
:
[1, 2, NaN].includes(NaN); // true
Также в отличие indexOf
, includes
не пропускает пропущенные индексы:
new Array(5).includes(undefined); // true
В настоящее время это все еще черновик, но его можно заполнить, чтобы он работал во всех браузерах.
b
это значение, и a
это массив. Возвращается true
или же false
:
function(a, b) {
return a.indexOf(b) != -1
}
Вот JavaScript 1.6 совместимая реализация Array.indexOf
:
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;
};
}
Использование:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
// Usage
if(isInArray(my_array, "my_value"))
{
//...
}
Расширение JavaScript Array
объект - действительно плохая идея, потому что вы вводите новые свойства (ваши пользовательские методы) в for-in
циклы, которые могут сломать существующие скрипты. Несколько лет назад авторам библиотеки Prototype пришлось реорганизовать свою библиотечную реализацию, чтобы удалить именно такие вещи.
Если вам не нужно беспокоиться о совместимости с другим JavaScript, работающим на вашей странице, воспользуйтесь этим, в противном случае я бы порекомендовал более неуклюжее, но более безопасное автономное функциональное решение.
Спектакль
Сегодня, 2020.01.07, провожу тесты на MacOs HighSierra 10.13.6 на Chrome v78.0.0, Safari v13.0.4 и Firefox v71.0.0 для 15 выбранных решений. Выводы
- решения на основе
JSON
,Set
и на удивлениеfind
(K,N,O) самые медленные во всех браузерах - es6
includes
(F) работает быстро только на хроме - решения на основе
for
(C,D) иindexOf
(G,H) довольно быстро работают во всех браузерах на малых и больших массивах, поэтому, вероятно, они являются лучшим выбором для эффективного решения - решения, в которых индекс уменьшается во время цикла, (B) медленнее, вероятно, потому, что работает способ кеширования процессора.
- Я также провожу тест для большого массива, когда искомый элемент находился на позиции 66% длины массива, и решения, основанные на
for
(C,D,E) дает аналогичные результаты (~630 операций / сек, но E в Safari и Firefox был на 10-20% медленнее, чем C и D)
Полученные результаты
Детали
Я выполняю 2 тестовых случая: для массива с 10 элементами и массива с 1 миллионом элементов. В обоих случаях мы помещаем искомый элемент в середину массива.
let log = (name,f) => console.log(`${name}: 3-${f(arr,'s10')} 's7'-${f(arr,'s7')} 6-${f(arr,6)} 's3'-${f(arr,'s3')}`)
let arr = [1,2,3,4,5,'s6','s7','s8','s9','s10'];
//arr = new Array(1000000).fill(123); arr[500000]=7;
function A(a, val) {
var i = -1;
var n = a.length;
while (i++<n) {
if (a[i] === val) {
return true;
}
}
return false;
}
function B(a, val) {
var i = a.length;
while (i--) {
if (a[i] === val) {
return true;
}
}
return false;
}
function C(a, val) {
for (var i = 0; i < a.length; i++) {
if (a[i] === val) return true;
}
return false;
}
function D(a,val)
{
var len = a.length;
for(var i = 0 ; i < len;i++)
{
if(a[i] === val) return true;
}
return false;
}
function E(a, val){
var n = a.length-1;
var t = n/2;
for (var i = 0; i <= t; i++) {
if (a[i] === val || a[n-i] === val) return true;
}
return false;
}
function F(a,val) {
return a.includes(val);
}
function G(a,val) {
return a.indexOf(val)>=0;
}
function H(a,val) {
return !!~a.indexOf(val);
}
function I(a, val) {
return a.findIndex(x=> x==val)>=0;
}
function J(a,val) {
return a.some(x=> x===val);
}
function K(a, val) {
const s = JSON.stringify(val);
return a.some(x => JSON.stringify(x) === s);
}
function L(a,val) {
return !a.every(x=> x!==val);
}
function M(a, val) {
return !!a.find(x=> x==val);
}
function N(a,val) {
return a.filter(x=>x===val).length > 0;
}
function O(a, val) {
return new Set(a).has(val);
}
log('A',A);
log('B',B);
log('C',C);
log('D',D);
log('E',E);
log('F',F);
log('G',G);
log('H',H);
log('I',I);
log('J',J);
log('K',K);
log('L',L);
log('M',M);
log('N',N);
log('O',O);
This shippet only presents functions used in performance tests - it not perform tests itself!
Массив маленький - 10 элементов
Вы можете провести тесты на своей машине ЗДЕСЬ
Массив большой - 1.000.000 элементов
Вы можете провести тесты на своей машине ЗДЕСЬ
Один лайнер:
function contains(arr, x) {
return arr.filter(function(elem) { return elem == x }).length > 0;
}
Подумав секунду, если вы делаете этот вызов много раз, гораздо эффективнее использовать ассоциативный массив Map для поиска с использованием хеш-функции.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
Я использую следующее:
Array.prototype.contains = function (v) {
return this.indexOf(v) > -1;
}
var a = [ 'foo', 'bar' ];
a.contains('foo'); // true
a.contains('fox'); // false
function contains(a, obj) {
return a.some(function(element){return element == obj;})
}
Array.prototype.some() был добавлен к стандарту ECMA-262 в 5-м издании
Если вы используете JavaScript 1.6 или новее (Firefox 1.5 или новее), вы можете использовать Array.indexOf. В противном случае, я думаю, вы получите что-то похожее на ваш оригинальный код.
Есть несколько вариантов сделать это, включая
includes
,
some
,
find
,
findIndex
.
Надеюсь, более быстрый двунаправленный indexOf
/ lastIndexOf
альтернатива
2015
В то время как новый метод включает в себя очень хорошо, поддержка пока практически нулевая.
Давно я думал о том, как заменить медленные функции indexOf/lastIndexOf.
Эффективный путь уже найден, глядя на топовые ответы. Из тех, кого я выбрал contains
Функция размещена @Damir Zekic, которая должна быть самой быстрой. Но в нем также говорится, что показатели взяты с 2008 года и поэтому устарели.
Я тоже предпочитаю while
над for
, но по непонятной причине я закончил написание функции с помощью цикла for. Это также может быть сделано с while --
,
Мне было любопытно, если итерация была бы намного медленнее, если я проверяю обе стороны массива, делая это. По-видимому, нет, и поэтому эта функция примерно в два раза быстрее, чем те, которые проголосовали сверху. Очевидно, что это также быстрее, чем родной. Это в реальной среде, где вы никогда не узнаете, находится ли искомое значение в начале или в конце массива.
Когда вы знаете, что вы только что выдвинули массив со значением, использование lastIndexOf остается, вероятно, лучшим решением, но если вам нужно перемещаться по большим массивам, и результат может быть везде, это может быть надежным решением для ускорения работы.
Двунаправленный 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 (так в основном 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');
Как это возможно?
Я думаю, что простой расчет для получения отраженного индекса в массиве настолько прост, что он в два раза быстрее, чем выполнение реальной итерации цикла.
Вот сложный пример выполнения трех проверок за итерацию, но это возможно только при более длинных вычислениях, которые вызывают замедление кода.
Мы используем этот фрагмент (работает с объектами, массивами, строками):
/*
* @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
function inArray(elem,array)
{
var len = array.length;
for(var i = 0 ; i < len;i++)
{
if(array[i] == elem){return i;}
}
return -1;
}
Возвращает индекс массива, если найден, или -1, если не найден
Если вы неоднократно проверяете наличие объекта в массиве, возможно, вам стоит
- Поддержание сортировки массива всегда путем вставки сортировки в ваш массив (поместите новые объекты в нужное место)
- Сделать обновление объектов как удаление + сортировка операции вставки и
- Используйте бинарный поиск в вашем
contains(a, obj)
,
Решение, которое работает во всех современных браузерах:
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 на это.
Используйте функцию Лодаша.
Это сжато, точно и имеет большую межплатформенную поддержку.
Принятый ответ даже не соответствует требованиям.
Требования: Рекомендовать наиболее краткий и эффективный способ выяснить, содержит ли массив 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)
Простое решение для этого требования - использовать 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"}];
надеюсь, это поможет кому-нибудь.
Можно использовать 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'));
В то время как 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, например.)
У ECMAScript 6 есть элегантное предложение по поиску.
Метод find выполняет функцию обратного вызова один раз для каждого элемента, присутствующего в массиве, до тех пор, пока не найдет элемент, в котором функция обратного вызова возвращает истинное значение. Если такой элемент найден, find немедленно возвращает значение этого элемента. В противном случае поиск возвращает неопределенный. обратный вызов вызывается только для индексов массива, которым присвоены значения; он не вызывается для индексов, которые были удалены или которым никогда не присваивались значения.
Вот документация 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;
}
});
}
Использование:
var myArray = ['yellow', 'orange', 'red'] ;
alert(!!~myArray.indexOf('red')); //true
Чтобы точно знать, что tilde
~
делать в этот момент, обратиться к этому вопросу Что делает тильда, когда она предшествует выражению?,
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);
Удивлен, что этот вопрос до сих пор не добавлен последний синтаксис, добавив мои 2 цента.
Допустим, у нас есть массив объектов 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.включает в себя -> (возвращает истину или ложь)
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 в CB).
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 в CB).
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 принимают обратный вызов, мы можем извлечь любой объект (даже если у нас нет ссылки) из массива, творчески установив истинное условие.
ОК, вы можете просто оптимизировать свой код, чтобы получить результат! Есть много способов сделать это, которые чище и лучше, но я просто хотел получить ваш шаблон и применить к нему, используя 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;
}