Страницы

Поиск по вопросам

четверг, 28 ноября 2019 г.

Как узнать, есть ли элемент в массиве?

#javascript


Есть массив: ['dog', 'cat', 'hamster', 'bird', 'fish'], нужно вывести в консоль true,
если массив содержит dog и false — если такой элемент отсутствует, но насколько я понимаю
доступ к элементу в массиве проходит по номеру array[..], каким образом можно проверить
наличие того или иного элемента?
    


Ответы

Ответ 1



Используйте метод indexOf var animals = ['dog', 'cat', 'hamster', 'bird', 'fish']; alert( animals.indexOf( 'dog' ) != -1 );

Ответ 2



Маленькая интродукция Задача поиска элемента в массиве стоит перед всеми достаточно часто и хорошо бы расписать, как это можно сделать. Ищут обычно в массиве элементы типа Number, String или Object. Естественно, самый быстрый способ поиска - по элементам типа Number, сравнение числа, даже очень большого, происходит очень быстро, гораздо проще проверить один элемент, чем лексиграфически сравнивать строки, а с объектами вообще другая история. Если мы ищем именно тот объект, который мы добавили в массив, то есть сравниваем ссылки - это так же быстро, как и сравнивать числа, а вот если же надо искать по свойствам объекта, то это может весьма и весьма затянуться. В особо сложных случаях, советую составлять какой-нибудь хэш объекта и строить отдельным массив-карту, в которой уже спокойно искать всё, что надо найти. Разберем 6 способов сделать это на нативном JS разной новизны и 3 способа с их разбором на популярных фреймворках: jQuery, underscore и lodash. Часть первая, нативная, в стиле аллегро Для начала надо пройтись по родным возможностям языка и посмотреть, что можно сделать самим. Поиск в лоб Попробуем просто идти по элементам массива, пока мы не встретим то, что нам нужно. Как всегда самое простое решение является в среднем самым быстрым. function contains(arr, elem) { for (var i = 0; i < arr.length; i++) { if (arr[i] === elem) { return true; } } return false; } Работает везде. Сравнивает строго, с помощью ===. Легко можно заменить на ==, бывает полезно, когда элементы массива разных типов, но может замедлить поиск. Его можно и модифицировать, добавив возможность начинать поиск элемента с конца. Шикарно ищет цифры, строки. Немного расширив, можно добавить возможность поиска элемента по своему условию (это поможет нам искать по свойствам объекта или, например, первый элемент, который больше 100500): function contains(arr, pred) { for (var i = 0; i < arr.length; i++) { if (typeof pred == 'function' && pred(arr[i], i, arr) || arr[i] === elem) { return true; } } return false; } Array.prototype.indexOf() Array.prototype.indexOf(searchElement[, fromIndex = 0]) - старый добрый метод, заставляющий всех мучиться со своей -1 в случае, когда элемента нет. function contains(arr, elem) { return arr.indexOf(elem) != -1; } Поддерживается он везде, кроме IE <= 7, но это уже давно забытая шутка. Данный метод выполняет поиск элемента строго от меньшего индекса к большему, применяя при сравнении ===. Увы, по свойствам объекта мы так искать не сможем. arr.indexOf(searchElement, fromIndex) принимает два аргумента, первый searchElement - это элемент который ищем, а второй, fromIndex, индекс с которого начнем искать (отрицательный говорит интерпретатору начинать поиск с arr.length + fromIndex индекса). Аккуратней, если вы укажете fromIndex больше длины массива, метод нераздумывая вернёт -1. function contains(arr, elem, from) { return arr.indexOf(elem, from) != -1; } Array.prototype.lastIndexOf() Array.prototype.lastIndexOf(searchElement[, fromIndex = arr.length - 1]) - справедливости ради надо рассказать и про него. Работает полностью аналогично Array.prototype.indexOf(), но только полностью наоборот (поиск идет в обратном порядке и fromIndex изначально отсчитывается с конца). Заменил конструкцию ret != -1 на !!~ret ради забавы. function contains(arr, elem, from) { return !!~arr.lastIndexOf(elem, from); } Array.prototype.find() Array.prototype.find(callback[, thisArg]) - модный стильный и молодежный ES6, со всеми вытекающими: function contains(arr, elem) { return arr.find((i) => i === elem) != -1; } Возвращает элемент или -1, если ничего не найдено. Ищет с помощью callback(elem, index, arr), то есть, если эта функция вернет true, то это именно тот самый, искомый элемент. Конечно, эту функцию можно задавать самому, поэтому метод универсален. Array.prototype.findIndex() Array.prototype.findIndex(callback[, thisArg]) - полностью аналогичный предыдущему метод, за исключением того, что функция возвращает не элемент, а индекс. Забавы ради сделаю её с возможностью передать свою функцию: function contains(arr, pred) { var f = typeof pred == 'function' ? pred : ( i => i === pred ); return arr.findIndex(f) != -1; } Array.prototype.includes() Array.prototype.includes(searchElement[, fromIndex]) - а это уже ES7, с ещё пока оочень сырой поддержкой. Наконец-то у нас будет специальный метод, чтобы узнать, есть ли элемент в массиве! Поздравляю! arr.includes(elem); Это всё, что нужно, чтобы найти элемент. Аргументы у этой функции полностью аналогичны Array.prototype.indexOf(). А вот вернет он true в случае успеха и false в обратном. Естественно искать по свойствам объектов нельзя, для этого есть Array.prototype.find(). Должен быть самым быстрым, но... Возможно, что он и станет со временем самым быстрым. Часть вторая, со вниманием, но чужая и в стиле сонаты Теперь, наконец, можно поговорить об этой же теме, но в контексте парочки фреймворков! Говорят, что всегда хорошо посмотреть сначала, как делают другие, перед тем, как начнешь делать это сам. Может это откроет нам глаза на что-нибудь интересное! jQuery jQuery.inArray(value, array [, fromIndex ]) - между прочим весьма быстрый метод, по тестам. Использует внутри строгое равенство === и возвращает -1, если ничего не нашел, а если все таки нашёл, то вернет его индекс. Для удобства и одинаковости обернем её в функцию: function contains(arr, elem) { return jQuery.inArray( elem, arr) != -1; } А теперь поговорим, как она работает. Вот, что она представляет из себя в версии 2.1.3: inArray: function( elem, arr, i ) { return arr == null ? -1 : indexOf.call( arr, elem, i ); } Где indexOf это вот это: // Use a stripped-down indexOf as it's faster than native // http://jsperf.com/thor-indexof-vs-for/5 indexOf = function( list, elem ) { var i = 0, len = list.length; for ( ; i < len; i++ ) { if ( list[i] === elem ) { return i; } } return -1; } Забавный комментарий говорит, что так быстрее, чем родной Array.prototype.indexOf() (могу предположить, что из-за отсутствия всех проверок) и предлагает посмотреть тесты производительности. По сути - это самый первый способ из первой части. Underscore _.contains(list, value) - вот такой метод предлагает нам популярная библиотека для работы с коллекциями. То же самое, что и _.include(list, value). Использует === для сравнения. Вернёт true, если в list содержится элемент, который мы ищем. Если list является массивом, будет вызван метод indexOf. _.contains = _.include = function(obj, target) { if (obj == null) return false; if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; return any(obj, function(value) { return value === target; }); }; Где nativeIndexOf - штука, которая говорит, что Array.prototype.indexOf() существует, а obj.indexOf === nativeIndexOf говорит, что list - массив. Теперь понятно, почему этот метод медленнее, чем jQuery.inArray(), просто обертка над Array.prototype.indexOf(). Ничего интересного. Lodash _.includes(collection, target, [fromIndex=0]) - вот последняя надежда на новые мысли, от второй знаменитейшей библиотеки для работы с коллекциями. То же самое, что _.contains() и _.include(). Возвращает true, если содержит и false если нет. fromIndex индекс элемента, с которого начинаем поиск. function includes(collection, target, fromIndex, guard) { var length = collection ? getLength(collection) : 0; if (!isLength(length)) { collection = values(collection); length = collection.length; } if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) { fromIndex = 0; } else { fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); } return (typeof collection == 'string' || !isArray(collection) && isString(collection)) ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1) : (!!length && getIndexOf(collection, target, fromIndex) > -1); } guard - служебный аргумент. Сначала находится длина коллекции, выбирается fromIndex, а потом... нет, не Array.prototype.indexOf()! Для поиска в строке используется String.prototype.indexOf(), а мы идем дальше в _.getIndexOf() и в результате попадём в ло-дашскую имплементацию indexOf(): function indexOf(array, value, fromIndex) { var length = array ? array.length : 0; if (!length) { return -1; } if (typeof fromIndex == 'number') { fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; } else if (fromIndex) { var index = binaryIndex(array, value); if (index < length && (value === value ? (value === array[index]) : (array[index] !== array[index]))) { return index; } return -1; } return baseIndexOf(array, value, fromIndex || 0); } Она интересна тем, что fromIndex может принимать значения как Number, так и Boolean и если это всё таки значение булевого типа и оно равно true, то функция будет использовать бинарный поиск по массиву! Прикольно. Иначе же выполнится indexOf() попроще: function baseIndexOf(array, value, fromIndex) { if (value !== value) { return indexOfNaN(array, fromIndex); } var index = fromIndex - 1, length = array.length; while (++index < length) { if (array[index] === value) { return index; } } return -1; } Интересный ход для случая, когда мы ищем NaN (напомню, что из-за досадной ошибки он не равен сам себе). Выполнится indexOfNaN(), и действительно ни один из всех способов описанных ранее не смог бы найти NaN, кроме тех, естественно, где мы могли сами указать функцию для отбора элементов. Можно предложить просто обернуть _.indexOf() для поиска элемента: function contains(arr, elem, fromIndex) { return _.indexOf(arr, elem, fromIndex) != -1; } Где fromIndex будет либо индексом откуда начинаем искать, либо true, если мы знаем, что arr отсортирован. Заключение, хотя и в стиле интермеццо И да, все эти варианты имеют смысл, только если момент с поиском данных част в вашем алгоритме или поиск происходит на очень больших данных. Вот приведу ниже несколько тестов на поиск элементов типа Number и String в массивах длинной 1000000 (миллион) элементов для трех случаев, когда элемент находится вначале массива, в середине (можно считать за среднюю по палете ситуацию) и в конце (можно считать за время поиска отсутствующего элемента, кроме метода с Array.prototype.lastIndexOf()). Number, 1000000 элементов, искомый элемент в начале; Number, 1000000 элементов, искомый элемент в середине; Number, 1000000 элементов, искомый элемент в конце; String, 1000000 элементов, искомый элемент в начале; String, 1000000 элементов, искомый элемент в середине; String, 1000000 элементов, искомый элемент в конце. Результаты тестов могут сильно зависеть от версии браузера, да и от самих браузеров, как этого избежать не знаю, но рекомендую протестить на нескольких. Видно, что в среднем везде выигрывает первый способ из первой части (написанный собственноручно), а второе место обычно делят lоdash и Array.prototype.includes(). Да, прошу заметить, что если взбредёт в голову искать NaN, то это может не получиться почти во всех методах, так как NaN !== NaN.

Ответ 3



Еще такой вариант встречал: if(new RegExp(animals.join("|")).test("dog")) {

Ответ 4



animals.some(item => item === "dog"); Он будет искать вхождение и если найдет сразу вернет true и прекратит двежение, если нечего не найдет отдаст false

Комментариев нет:

Отправить комментарий