Лучший способ проверить, содержится ли элемент в массиве JavaScript?
Проблема: Как правильно проверить, содержится ли объект в массиве?
Я нашел один из самых простых способов сделать это:
function include(arr, obj) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] == obj) return true;
}
}
console.log(include([1, 2, 3, 4], 3)); // true
console.log(include([1, 2, 3, 4], 6)); // undefined
Однако я не уверен, что этот способ является наилучшим. Есть ли более эффективные или более оптимизированные методы для проверки наличия объекта в массиве?
5 ответ(ов)
Если вы используете jQuery, то ваш код:
$.inArray(5 + 5, [ "8", "9", "10", 10 + "" ]);
возвращает -1
, так как значение 10
(результат вычисления 5 + 5
) не найдено в массиве ["8", "9", "10", "10"]
. Метод $.inArray
ищет элемент в массиве и возвращает его индекс, если он найден, или -1
, если нет.
В данном случае [ "8", "9", "10", 10 + "" ]
представляет собой массив строк, и 10
(числовой тип) не соответствует строке "10"
из массива, так как сравнение происходит по строгому равенству. Если вы хотите проверить наличие 10
в массиве, вам нужно будет использовать строку "10"
:
$.inArray("10", [ "8", "9", "10", 10 + "" ]); // Это вернет 2
Для получения дополнительной информации о методе, вы можете ознакомиться с документацией jQuery.
Если массив не отсортирован, на самом деле нет лучшего способа (за исключением использования упомянутого выше метода indexOf, который, на мой взгляд, дает тот же результат). Если же массив отсортирован, можно использовать бинарный поиск, который работает так:
- Выберите средний элемент массива.
- Является ли элемент, который вы ищете, больше выбранного вами элемента? Если да, то вы исключили нижнюю половину массива. Если нет, то исключили верхнюю половину.
- Выберите средний элемент оставшейся половины массива и продолжайте, как в шаге 2, исключая половины оставшегося массива. В конечном итоге вы либо найдете нужный элемент, либо у вас не останется массива для поиска.
Бинарный поиск работает за время, пропорциональное логарифму длины массива, поэтому он может быть значительно быстрее, чем просмотр каждого отдельного элемента.
Вам нужно реализовать метод has
для массива, который будет проверять наличие элемента в массиве. Чтобы избежать добавления перечисляемого свойства к каждому массиву, вы уже правильно используете Object.defineProperty
. Вот пример реализации этого метода, который поддерживает как примитивные значения, так и сравнение объектов по значению, если передан второй аргумент flag
.
Вот ваш код с некоторыми комментариями:
Object.defineProperty(Array.prototype, 'has', {
value: function(o, flag) {
// Если флаг не передан, просто используем indexOf для проверки примитивных значений
if (flag === undefined) {
return this.indexOf(o) !== -1;
} else { // Только для сырых объектов JavaScript
for (var v in this) {
// Сравниваем строки, формируемые с помощью JSON.stringify
if (JSON.stringify(this[v]) === JSON.stringify(o)) return true;
}
return false;
}
},
// writable: false,
// enumerable: false,
// configurable: false
});
Вы можете использовать этот метод следующим образом:
console.log([22, 'a', {prop:'x'}].has(12)); // false
console.log(["a", "b"].has("a")); // true
let obj1 = {a: 1};
let obj2 = {a: 1};
console.log([1, obj1].has(obj2, 1)); // true (значения равны)
console.log([1, obj1].has(obj2)); // false (сравнение по ссылке)
Значение второго аргумента (flag
) заставляет метод сравнивать значения объектов, а не их ссылки. Таким образом, вот пример:
let o1 = {a: 1, b: {c: 2}};
let o2 = {a: 1, b: {c: 2}};
console.log([o1].has(o2, true)); // true, если значения на каждом уровне одинаковы
Это решение эффективно позволяет проверять наличие элементов, избегая добавления новых методов в прототип объектов, что гарантирует, что ваши массивы останутся "незабитыми" новыми свойствами.
Это зависит от вашей цели. Если вы программируете для веба, избегайте использования indexOf
, так как он не поддерживается в Internet Explorer 6 (многие из них все еще используются!). Вместо этого можно использовать условный подход:
if (yourArray.indexOf !== undefined) result = yourArray.indexOf(target);
else result = customSlowerSearch(yourArray, target);
indexOf
, вероятно, реализован на нативном коде, поэтому он быстрее, чем что-либо, что вы можете сделать на JavaScript (за исключением двоичного поиска, если массив подходит для этого).
Примечание: это вопрос вкуса, но я бы добавил return false;
в конце вашей функции, чтобы получить истинное булевое значение...
Вот полезная информация для вас: если хотите узнать, что можно делать с массивом в JavaScript, лучше всего обратиться к документации. Вот страница массива на сайте Mozilla:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array
Там вы найдете упоминание о методе indexOf, который был добавлен в JavaScript 1.6.
Как перемешать (сделать случайным) массив в JavaScript?
Как объединить два массива в JavaScript и удалить дубликаты?
Как очистить массив в JavaScript?
Как удалить все дубликаты из массива объектов?
Выбор последнего элемента в массиве JavaScript