Как проверить, содержит ли массив значение в JavaScript?

4002

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

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

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

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

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

штифтик
источник
49
только что протестировал: ваш путь на самом деле самый быстрый для всех браузеров: jsperf.com/find-element-in-obj-vs-array/2 (кроме предварительного сохранения a.length в переменной) при использовании indexOf (как в $ .inArray) намного медленнее
Йорн Беркефельд
17
многие ответили, что Array # indexOf - ваш лучший выбор здесь. Но если вы хотите что-то, что может быть правильно приведено к Boolean, используйте это: ~[1,2,3].indexOf(4)вернет 0, что будет оцениваться как ложь, тогда как ~[1,2,3].indexOf(3)вернет -3, что оценит как истину.
Lordvlad
8
~это не то, что вы хотите использовать для преобразования в логическое значение, для этого вам нужно !. Но в этом случае вы хотите проверить равенство с -1, чтобы функция могла заканчиваться return [1,2,3].indexOf(3) === -1; ~двоичным кодом, она будет инвертировать каждый бит значения по отдельности.
mcfedr
14
@Iordvlad на [1,2,3].indexOf(4)самом деле вернет -1 . Как указал @mcfedr, ~является побитовым оператором NOT , см. ES5 11.4.8. Дело в том, что, поскольку двоичное представление -1состоит только из 1, его дополнение есть 0, которое оценивается как ложное. Дополнение любого другого числа будет отличным от нуля, следовательно, истина. Итак, ~работает просто отлично и часто используется вместе с indexOf.
mknecht
5
Название вводит в заблуждение. Где находится [[1,2],[3,4]].includes([3,4])?
mplungjan

Ответы:

4381

Современные браузеры имеют Array#includes, что делает именно это и широко поддерживается всеми, кроме IE:

console.log(['joe', 'jane', 'mary'].includes('jane')); //true

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


Многие фреймворки также предлагают похожие методы:

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

кодеап
источник
42
MooTools также имеет Array.contains, который возвращает логическое значение, которое здесь звучит как настоящий вопрос.
Райан Флоренс
22
Прототип также имеет, Array.includeчто возвращает логическое значение
user102008
46
Если вы используете хороший браузер, вы можете просто использоватьarray.indexOf(object) != -1
Сэм Соффс
13
Кроме того, не используйте в качестве условия только indexOf, поскольку первый элемент вернет 0 и будет оценен как ложный
плюс
241
inArrayужасное имя для функции, которая возвращает индекс элемента, и -1если он не существует. Я ожидаю, что логическое значение будет возвращено.
Тим
434

Обновление с 2019 года: Этот ответ с 2008 года (11 лет!) И не имеет отношения к современному использованию JS. Обещанное улучшение производительности было основано на тесте, сделанном в браузерах того времени. Это может не относиться к современным контекстам выполнения JS. Если вам нужно простое решение, ищите другие ответы. Если вам нужна лучшая производительность, оцените себя в соответствующих средах исполнения.

Как уже говорили другие, итерация по массиву, вероятно, является наилучшим способом, но было доказано, что убывающий 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
Дамир Зекич
источник
24
Но будьте осторожны: stackoverflow.com/questions/237104/javascript-array-containsobj/…
MatrixFrog
22
«Проверено» - это сильное слово. Двигатели JS постоянно совершенствуются, а время выполнения, измеренное 3 года назад, ужасно устарело.
orip
2
@ Дамир - я согласен. Возможно, измените пример, чтобы использовать indexOf, если он доступен, просто чтобы люди, слепо копирующие этот код, получали максимальную производительность, какую только могли.
orip
1
@cbmeeks да, забота определенно необходима. Вероятно, это был случай, for (o in array)который не следует делать при циклическом прохождении массива ...
Дамир Зекич
1
Лучший способ сделать это - проверить, если [1, 2, 3] .indexOf (1)> -1
Devin G Rhode
207

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 (например, этот ).

CIC
источник
на самом деле, есть пример реализации расширения indexOf для браузеров, которые не поддерживают его, на странице developer.mozilla.org, на которую вы ссылаетесь.
Ллойд Коттен
на самом деле, если вы добавите indexof к прототипу Array для браузеров, которые его не поддерживают (т.е. IE7), они также попытаются перебрать эту функцию при циклическом просмотре элементов в массиве. противно.
CpILL
это применимо для проверки на предмет? я не думаю, что это работает в случае Объекта
Химеш Аадесхара
169

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

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

Ориоль
источник
3
Не поддерживается для IE и Microsfot Edge (2015) ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
Адриано Резенде,
1
Также имеет значение таблица совместимости ES7 (похоже, что Chrome поддерживает ее сейчас)
стиль
это применимо для проверки на предмет? я не думаю, что это работает в случае Объекта
Химеш Аадесхара
128

Верхние ответы предполагают примитивные типы, но если вы хотите узнать, содержит ли массив объект с некоторой чертой, 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

Майкл
источник
1
На сегодняшний день, сентябрь 2018 года, Array.prototype.some () полностью поддерживается: таблица поддержки
caniuse.com
1
Работа в Node> = 8.10 для AWS Node.js Lambda, так что это здорово. Очень чистое и простое решение! J
Иордания
1
@jamess Это может быть хорошо поддержано, но помните, что Arrow functionsв этом примере это не так хорошо поддерживается. Для получения более подробной информации смотрите здесь: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Камил Витковски
Есть ли какое-нибудь короткое замыкание? Или он повторяет весь массив, даже если он нашел значение?
Дуглас Гаскелл
@DouglasGaskell прерывает итерацию, найденную (упомянуто в ответе)
Майкл
112

Допустим, вы определили массив следующим образом:

const array = [1, 2, 3, 4]

Ниже приведены три способа проверить, есть ли 3там. Все они возвращают либо trueили false.

Метод Native Array (начиная с ES2016) ( таблица совместимости )

array.includes(3) // true

Как пользовательский метод Array (до ES2016)

// Prefixing the method with '_' to avoid name clashes
Object.defineProperty(Array.prototype, '_includes', { value: function (v) { return this.indexOf(v) !== -1 }})
array._includes(3) // true

Простая функция

const includes = (a, v) => a.indexOf(v) !== -1
includes(array, 3) // true
Уильям Мало
источник
Он возвращает true, если "b" находится в массиве "a" ... Я не знаю, как еще это объяснить ...
Уильям Мало
4
Эту часть я не понимаю "!! ~". И я думаю, что это не будет работать в IE8, потому что IE8 не поддерживает indexOf () для объекта Array.
свида
62
«~» - оператор, который вычитает, инвертирует и вычитает 1 из числа. indexOf возвращает -1 в случае неудачи, поэтому «~» превращает -1 в «0». с помощью "!!" витки номера в boleans (!! 0 === ложь)
уилльям Malo
1
Круто, но серьезно ради простоты у не только a.indexOf (b)> - 1, так как "> -1" .length === "!! ~" .length
супер
2
Я бы назвал отсутствие знаний о влиянии логических операторов непрофессиональным. Но я согласен с ценностью читабельного кода, я бы наверняка обернул это в четко обозначенную функцию. И это именно то, что делают большинство основных JS-фреймворков.
okdewit
79

Вот совместимая с 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;
        };
}
Мар Эрлигссон
источник
Это выглядит великолепно, но немного запутанно: * Разве тесты в строках 1 и 3 не эквивалентны? * Не лучше ли протестировать прототип и, если необходимо, добавить функцию в Array.prototype?
Ави Лен
10
Они не эквивалентны. [].indexOfэто сокращение для Array.prototype.indexOf. Мы, параноидальные защитники Javascript, избегаем расширения собственных прототипов любой ценой.
Мар Эрлигссон
1
Разве не [].indexOfсоздается новый массив, а затем осуществляется доступ indexOf, в то время Array.prototype.indexOfкак он напрямую обращается к прототипу?
Алекс
3
@alex да [].indexOf === Array.prototype.indexOf(попробуйте в FireBug), но наоборот [].indexOf !== Array.indexOf.
Мар Örlygsson
57

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

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

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}
Матиас Канепа
источник
25
x ? true : falseобычно излишним. Это здесь.
Ry-
@minitech Почему вы говорите, что это избыточно?
Матиас Канепа
8
array.indexOf(search) >= 0уже логическое. Просто return array.indexOf(search) >= 0.
Ry-
@minitech хорошо, спасибо! На самом деле я не знал, что такая конструкция может быть возвращена. Пока что-то новое.
Матиас Канепа
Буквально любая конструкция в javascript может быть возвращена
BT
49

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

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

Питер Мортенсен
источник
22
Я не согласен. По этой причине циклы for-in не должны использоваться для массивов. Использование циклов for-in прекратится при использовании одной из популярных библиотек js
Томас
Будет ли это считаться исправлением обезьяны? LOL Некоторые люди, как это.
cbmeeks
33

Один лайнер:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}
AlonL
источник
8
array.filter(e=>e==x).length > 0эквивалентно, array.some(e=>e==x)но someболее эффективно
Аполо
32

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

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

MattMcKnight
источник
Хотя это очевидно полезно для многих, было бы лучше, если бы был добавлен фрагмент кода.
Пирог «О» Пах
28

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

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

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

a.contains('foo'); // true
a.contains('fox'); // false
Эдуардо Куомо
источник
24
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

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

dansalmo
источник
при использовании es6 его можно сократить какcontains = (a, obj) => a.some((element) => element === obj))
diEcho
Даже IE9 поддерживает Array.prototype.some (), начиная с ECMAScript 5 .
Suncat2000
19

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

2015

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

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

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

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

Как это возможно?

Я думаю, что простой расчет для получения отраженного индекса в массиве настолько прост, что он в два раза быстрее, чем выполнение реальной итерации цикла.

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

http://jsperf.com/bidirectionalindexof/2

кокко
источник
18

Представление

Сегодня 2020.01.07 я выполняю тесты на MacO HighSierra 10.13.6 на Chrome v78.0.0, Safari v13.0.4 и Firefox v71.0.0 для 15 выбранных решений. Выводы

  • решения , основанные на JSON, Setи удивительно find(К, N, О) является самым медленным на все браузеры
  • 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 миллионом элементов. В обоих случаях мы помещаем искомый элемент в середину массива.

Массив малый - 10 элементов

Вы можете выполнить тесты на своем компьютере ЗДЕСЬ

введите описание изображения здесь

Массив большой - 1.000.000 элементов

Вы можете выполнить тесты на своем компьютере ЗДЕСЬ

введите описание изображения здесь

Камил Келчевски
источник
лолз, это так долго
да кокосовый орех
16

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

Андру Лувизи
источник
16
function inArray(elem,array)
{
    var len = array.length;
    for(var i = 0 ; i < len;i++)
    {
        if(array[i] == elem){return i;}
    }
    return -1;
} 

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

LMC
источник
16

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

/*
 * @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
dr.dimitru
источник
15

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

  1. Поддержание сортировки массива всегда путем вставки сортировки в ваш массив (поместите новые объекты в нужное место)
  2. Сделать обновление объектов как удаление + сортировка операции вставки и
  3. Используйте бинарный поиск в вашем contains(a, obj).
Ztyx
источник
2
Или, если возможно, полностью прекратите использование массива и вместо этого используйте объект в качестве словаря, как предложили MattMcKnight и ninjagecko.
Joeytwiddle
13

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

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функция с улучшенной оптимизацией. Спасибо, что указали на это.

Игорь Барбашин
источник
Этот конкретный кусок кода может работать в IE6 (не проверял), но IE не поддерживал ES5 до IE9.
Марк Рид,
Из соображений производительности следует избегать строковых. По крайней мере, вы должны избегать JSON.stringify «obj» в каждом цикле, потому что это дорого и замедлит ваше приложение. Для этого вы должны
записать
1
@ Хорошее замечание. Обновил includesфункцию с вашим предложением. Я запустил jsperf с моей функцией. Это примерно в 5 раз медленнее, чем включает Лодаш. Хотя lodash не сравнивается по значению и не может найти {a: 1}в [{a: 1}]. Я не знаю, делает ли это какая-нибудь библиотека. Но мне любопытно, есть ли более эффективный и не безумно сложный способ сделать это.
Игорь Барбашин
Позднее примечание: это не работает, скажем, contains([{ a: 1, b: 2 }], { b: 2, a: 1 })потому что строковые объекты поддерживают порядок свойств.
Еретик Обезьяна
1
@ HereticMonkey, правда. Вот почему я добавил sort-keysпримечание внизу
Игорь Барбашин
12

Используйте lodash это некоторая функция.

Это сжато, точно и имеет большую поддержку кроссплатформенности.

Принятый ответ даже не соответствует требованиям.

Требования: Рекомендовать наиболее краткий и эффективный способ выяснить, содержит ли массив 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)
L3x
источник
10

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

Метод find выполняет функцию обратного вызова один раз для каждого элемента, присутствующего в массиве, пока не найдет элемент, в котором callback возвращает истинное значение. Если такой элемент найден, 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;
    }
  });
}
Прадип Махдеву
источник
Теперь это стандарт: ecma-international.org/ecma-262/6.0/#sec-array.prototype.find
Madbreaks
9

Хотя 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, например.)

ninjagecko
источник
9

Можно использовать 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'));

rlib
источник
5
Я думаю, что return proxy.has(obj)это намного чище, чем две строки с оператором if-else здесь
Maciej Bukowski
function contains(arr, obj) { return new Set(arr).has(obj); }
Гордон Бин
8

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

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

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

демонстрация

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

Мина Габриэль
источник
5
Это было опубликовано полтора года назад, повторять не нужно.
Shadow Wizard - это ухо для тебя
3
На самом деле, он не был опубликован. Не как ответ, а как комментарий к ответу, и даже тогда это не ясно и кратко. Спасибо за публикацию, Мина Габриэль.
T.CK
6

ОК, вы можете просто оптимизировать свой код, чтобы получить результат!

Есть много способов сделать это, которые будут чище и лучше, но я просто хотел получить ваш паттерн и применить его 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;
}
Алиреза
источник
Позднее примечание: это не работает, скажем, contains([{ a: 1, b: 2 }], { b: 2, a: 1 })потому что строковые объекты поддерживают порядок свойств.
Еретик Обезьяна
5

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

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

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

sqram
источник
Что вы должны использовать, если не это?
БРИКа
@ bryc, может быть, принятое решение или другое решение отсюда. Если вам не безразлична производительность, вы можете использовать это
sqram
5

Удивлен, что к этому вопросу еще не добавлен последний синтаксис, добавив мои 2 цента.

Допустим, у нас есть массив Objects 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 принимают обратный вызов, мы можем извлечь любой объект (даже если у нас нет ссылки) из массива, творчески установив истинное условие.

Шумер
источник
5

Простое решение для этого требования использует 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');

если data имеет значение null, то нет администратора, иначе он вернет существующий объект, как показано ниже.

{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"}];

надеюсь, это кому-нибудь поможет.

Шива
источник
5

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

Санджай Магар
источник