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

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

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

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

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

4539
brad 26 Окт 2008 в 02:14

27 ответов

Лучший ответ

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

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

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


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

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

4305
33 revs, 19 users 29% 11 Фев 2020 в 19:13

Используйте функцию 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)
11
l3x 21 Окт 2015 в 11:58

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

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

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}
55
Peter Mortensen 7 Янв 2017 в 11:29

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

121
Michael 5 Сен 2019 в 11:17

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

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

166
Oriol 8 Фев 2016 в 16:53

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

201
Peter Mortensen 11 Авг 2011 в 23:41

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

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

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

Метод собственных массивов (начиная с 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
111
Gust van de Wal 13 Ноя 2019 в 17:09

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

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

13
Igor Barbashin 4 Июл 2017 в 23:34

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

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

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

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

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

8
Community 23 Май 2017 в 12:03

Вот реализация 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;
        };
}
77
Narendra Jadhav 30 Янв 2019 в 10:23

Можно использовать набор, в котором есть метод " ( ) " :

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
J D 24 Янв 2020 в 12: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, если не найден

15
mpromonet 6 Сен 2014 в 16:22
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

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

23
dansalmo 12 Сен 2014 в 16:55

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

14
Andru Luvisi 25 Окт 2008 в 22:44

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

/*
 * @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
15
dr.dimitru 10 Сен 2014 в 12:12

У 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;
    }
  });
}
9
Peter Mortensen 7 Янв 2017 в 11:27

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

  1. Постоянно сортируйте массив, выполняя сортировку вставками в вашем массиве (добавьте новые объекты на нужное место)
  2. Сделать обновление объектов как удаление + сортировка операции вставки и
  3. Используйте бинарный поиск в своем contains(a, obj) поиске.
14
Ztyx 5 Фев 2011 в 18:02

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

Есть много способов сделать это, которые будут чище и лучше, но я просто хотел получить ваш шаблон и применить к нему, используя 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;
}
6
Alireza 6 Фев 2019 в 14:48

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

9
Peter Mortensen 7 Янв 2017 в 11:30

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

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

48
Peter Mortensen 11 Авг 2011 в 23:43

Обновление с 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
428
Damir Zekić 7 Авг 2019 в 10:49

Простое решение для этого требования использует 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
Shiva 11 Окт 2019 в 17:38
    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);
5
J D 24 Янв 2020 в 12:01

Производительность

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

Полученные результаты

enter image description here

Детали

Я выполняю 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 элементов

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

enter image description here

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

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

enter image description here

11
Kamil Kiełczewski 11 Мар 2020 в 16:26

Один лайнер:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}
31
Peter Mortensen 7 Янв 2017 в 11:25

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

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

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

a.contains('foo'); // true
a.contains('fox'); // false
26
Eduardo Cuomo 15 Июн 2014 в 01:15