If you don’t need type coercion (because of the use of indexOf
), you could try something like the following:
var arr = [1, 2, 3];
var check = [3, 4];
var found = false;
for (var i = 0; i < check.length; i++) {
if (arr.indexOf(check[i]) > -1) {
found = true;
break;
}
}
console.log(found);
Where arr
contains the target items. At the end, found
will show if the second array had at least one match against the target.
Of course, you can swap out numbers for anything you want to use — strings are fine, like your example.
And in my specific example, the result should be true
because the second array’s 3
exists in the target.
UPDATE:
Here’s how I’d organize it into a function (with some minor changes from before):
var anyMatchInArray = (function () {
"use strict";
var targetArray, func;
targetArray = ["apple", "banana", "orange"];
func = function (checkerArray) {
var found = false;
for (var i = 0, j = checkerArray.length; !found && i < j; i++) {
if (targetArray.indexOf(checkerArray[i]) > -1) {
found = true;
}
}
return found;
};
return func;
}());
DEMO: http://jsfiddle.net/u8Bzt/
In this case, the function could be modified to have targetArray
be passed in as an argument instead of hardcoded in the closure.
UPDATE2:
While my solution above may work and be (hopefully more) readable, I believe the «better» way to handle the concept I described is to do something a little differently. The «problem» with the above solution is that the indexOf
inside the loop causes the target array to be looped over completely for every item in the other array. This can easily be «fixed» by using a «lookup» (a map…a JavaScript object literal). This allows two simple loops, over each array. Here’s an example:
var anyMatchInArray = function (target, toMatch) {
"use strict";
var found, targetMap, i, j, cur;
found = false;
targetMap = {};
// Put all values in the `target` array into a map, where
// the keys are the values from the array
for (i = 0, j = target.length; i < j; i++) {
cur = target[i];
targetMap[cur] = true;
}
// Loop over all items in the `toMatch` array and see if any of
// their values are in the map from before
for (i = 0, j = toMatch.length; !found && (i < j); i++) {
cur = toMatch[i];
found = !!targetMap[cur];
// If found, `targetMap[cur]` will return true, otherwise it
// will return `undefined`...that's what the `!!` is for
}
return found;
};
DEMO: http://jsfiddle.net/5Lv9v/
The downside to this solution is that only numbers and strings (and booleans) can be used (correctly), because the values are (implicitly) converted to strings and set as the keys to the lookup map. This isn’t exactly good/possible/easily done for non-literal values.
Here’s a way I am doing it after researching it for a while. I wanted to make a Laravel API endpoint that checks if a field is «in use», so the important information is: 1) which DB table? 2) what DB column? and 3) is there a value in that column that matches the search terms?
Knowing this, we can construct our associative array:
$SEARCHABLE_TABLE_COLUMNS = [
'users' => [ 'email' ],
];
Then, we can set our values that we will check:
$table = 'users';
$column = 'email';
$value = 'alice@bob.com';
Then, we can use array_key_exists()
and in_array()
with eachother to execute a one, two step combo and then act upon the truthy
condition:
// step 1: check if 'users' exists as a key in `$SEARCHABLE_TABLE_COLUMNS`
if (array_key_exists($table, $SEARCHABLE_TABLE_COLUMNS)) {
// step 2: check if 'email' is in the array: $SEARCHABLE_TABLE_COLUMNS[$table]
if (in_array($column, $SEARCHABLE_TABLE_COLUMNS[$table])) {
// if table and column are allowed, return Boolean if value already exists
// this will either return the first matching record or null
$exists = DB::table($table)->where($column, '=', $value)->first();
if ($exists) return response()->json([ 'in_use' => true ], 200);
return response()->json([ 'in_use' => false ], 200);
}
// if $column isn't in $SEARCHABLE_TABLE_COLUMNS[$table],
// then we need to tell the user we can't proceed with their request
return response()->json([ 'error' => 'Illegal column name: '.$column ], 400);
}
// if $table isn't a key in $SEARCHABLE_TABLE_COLUMNS,
// then we need to tell the user we can't proceed with their request
return response()->json([ 'error' => 'Illegal table name: '.$table ], 400);
I apologize for the Laravel-specific PHP code, but I will leave it because I think you can read it as pseudo-code. The important part is the two if
statements that are executed synchronously.
array_key_exists()
andin_array()
are PHP functions.
source:
-
https://php.net/manual/en/function.array-key-exists.php
-
https://php.net/manual/en/function.in-array.php
The nice thing about the algorithm that I showed above is that you can make a REST endpoint such as GET /in-use/{table}/{column}/{value}
(where table
, column
, and value
are variables).
You could have:
$SEARCHABLE_TABLE_COLUMNS = [
'accounts' => [ 'account_name', 'phone', 'business_email' ],
'users' => [ 'email' ],
];
and then you could make GET requests such as:
GET /in-use/accounts/account_name/Bob's Drywall
(you may need to uri encode the last part, but usually not)
GET /in-use/accounts/phone/888-555-1337
GET /in-use/users/email/alice@bob.com
Notice also that no one can do:
GET /in-use/users/password/dogmeat1337
because password
is not listed in your list of allowed columns for user
.
Good luck on your journey.
In JavaScript, you can check if every element of the first array exists in the second array, in the following ways:
- Using
Array.prototype.every()
; - Using a Loop.
Using Array.prototype.every()
To check if every element of the first array exists in the second array, you can do the following:
- Use
Array.prototype.every()
method to check whether all elements in an array pass the test implemented by the provided function; - Use
Array.prototype.includes()
(orArray.prototype.indexOf()
in the callback ofArray.prototype.every()
to check if the current element of first array exists in the second array.
For example, you can implement this in the following way:
// ES7+ const arr1 = [ 1, 2, 3 ]; const arr2 = [ 3, 5, 4, 2, 7, 0, 1, 10 ]; const hasAllElems = arr1.every(elem => arr2.includes(elem)); console.log(hasAllElems); // true
If one or more elements do not exist in the second array, then false
is returned (as you can see in the example below):
// ES7+ const arr1 = [ 1, 2, 16 ]; const arr2 = [ 3, 5, 4, 2, 7, 0, 1, 10 ]; const hasAllElems = arr1.every(elem => arr2.includes(elem)); console.log(hasAllElems); // false
If you are unable to support ES7, then you can achieve the same in ES5 using Array.prototype.indexOf()
(instead of Array.prototype.includes()
), for example, like so:
// ES5+ const arr1 = [ 1, 2, 3 ]; const arr2 = [ 3, 5, 4, 2, 7, 0, 1, 10 ]; const hasAllElems = arr1.every(function (elem) { return arr2.indexOf(elem) > -1; }); console.log(hasAllElems); // true
If you are able to support ES6, then you can rewrite the callback to Array.prototype.every()
as an arrow function to make it more compact.
Using a Loop
To check if every element of the first array exists in the second array, you can do the following:
- Use a loop (such as a
for
loop) and iterate over the first array; - In each iteration, use
Array.prototype.indexOf()
(orArray.prototype.includes()
) to check if the current element of first array exists in the second array; - Return
true
from the callback if all elements of first array exist in the second array, otherwise, returnfalse
.
For example, you can implement this using a for
loop and Array.prototype.indexOf()
in the following way:
const arr1 = [ 1, 2, 3 ]; const arr2 = [ 3, 5, 4, 2, 7, 0, 1, 10 ]; let hasAllElems = true; for (let i = 0; i < arr1.length; i++){ if (arr2.indexOf(arr1[i]) === -1) { hasAllElems = false; break; } } console.log(hasAllElems); // true
This can be especially useful if you are unable to support a minimum ES5 as with the other method.
Using the same code, you can see that if one or more elements do not exist in the second array, then false
is returned:
const arr1 = [ 1, 2, 16 ]; const arr2 = [ 3, 5, 4, 2, 7, 0, 1, 10 ]; let hasAllElems = true; for (let i = 0; i < arr1.length; i++){ if (arr2.indexOf(arr1[i]) === -1) { hasAllElems = false; break; } } console.log(hasAllElems); // false
You can also create a function for this like so:
function arrayContainsAll(needle, haystack) { for (let i = 0; i < needle.length; i++){ if (haystack.indexOf(needle[i]) === -1) { return false; } } return true; } const result1 = arrayContainsAll([ 1, 2, 3 ], [ 3, 5, 4, 2, 7, 0, 1, 10 ]); const result2 = arrayContainsAll([ 1, 2, 16 ], [ 3, 5, 4, 2, 7, 0, 1, 10 ]); console.log(result1); // true console.log(result2); // false
Hope you found this post useful. It was published 26 Dec, 2020 (and was last revised 02 Feb, 2023). Please show your love and support by sharing this post.
Если @egor_nullptr вас понял правильно, и вам нужно проверить, что массив b хотя бы в одном экзепляре содержит каждый элемент массива a, то предлагаю такое решение:
Array.prototype.hasAll = function(a) {
var hash = this.reduce(function(acc, i) { acc[i] = true; return acc; }, {});
return a.every(function(i) { return i in hash; });
};
По сравнению с решением @egor_nullptr оно асимптотически быстрее: O(n + m) против O(n * m), где n, m — размеры массивов a, b. При n = m = 100 000 на моей машине мой вариант отрабатывает быстрее в 200 раз. Тестировал так.
Знаете, каков мой опыт разработки на JavaScript? Я программирую на нём 40 минут, в обнимку с JavaScript Reference от Mozilla. Мой основной язык — C++. А вам очень советую добраться-таки до какой-нибудь книжки по алгоритмам и структурам данных (например, до «Introduction to Algorithms». Она есть на русском, лучший перевод, на мой взгляд, от МЦНМО). Это полезно, уверяю вас.
Эту задачу можно понять несколькими смыслами:
- Мы в принципе хотим понять существует ли массив с квадратными скобками в основном массиве
- Мы хотим понять, есть ли похожий массив в основном массиве (с теми же элементами и их значениями)
В этой публикации мы рассмотрим эти два варианта трактования вопроса. Начнём с простого.
Есть ли какой-то массив в основном массиве? Он существует в виде значения элемента основного массива?
Есть основной массив с одним объектом, одним массивом, числами и строками:
var massiv = [1, 2, 3, {a:"a"}, [777], 4, 5, "efim360.ru", "javascript"]
Мы будем проверять существование какого-либо массива в основном массиве при помощи метода filter(). Внутрь метода filter() мы будем передавать анонимную функцию, которая будет возвращать нам true или false по заданному условию. Если будет true, то элемент попадёт в новый массив (отфильтрованный), если false, то не попадёт.
Т. к. мы имеем дело с прототипами классов, то условие сравнения будет определяться при помощи «имени конструктора«, в котором этот элемент был создан. В качестве цели будет выбран класс Array, а его имя конструктора будет строкой «Array«. Не перепутайте, а то не получится. Мы сравниваем строки!
massiv.filter(i => i.constructor.name == "Array")
Мы вызвали filter() и в ответ получили новый массив (отфильтрованный) с одним элементом. Это как раз то, что мы искали. Мы отловили в основном массиве какой-то массив.
Мы имеем новый массив (отфильтрованный) длиной 1. Если бы в нашем основном массиве небыло бы массивов, тогда фильтр вернул бы нам массив нулевой длины. Вот это и будет нашим условием существования какого либо объекта в массиве:
(massiv.filter(i => i.constructor.name == "Array").length > 0)
Получили ИСТИНУ (TRUE) значит в основном массиве есть массив.
Справка
Мы всегда можем распознать объект по его принадлежности к классу. В нашем случае:
- фигурные скобки принадлежат классу Object,
- квадратные скобки принадлежат классу Array,
- числа принадлежат классу Number
- строки принадлежат классу String
Давайте убедимся, что это так. Мы будем получать класс каждого элемента. То есть мы будем получать название конструктора, в котором был создан данный элемент массива.
massiv.map(i=>[i, i.constructor.name])
Есть ли похожий массив в массиве?
На эту тему можно спорить долго. «Одинаковость» массивов я буду определять по Наборам элементов. Если наборы совпадают — значит элементы равны (очень похожи друг на друга). Вы можете придумать свой вариант схожести массивов.
Есть массив:
var massiv = [[1,2,3], [3,2,1], [1,2,3,4], 4, 5, 6, "efim360.ru", "javascript", true]
Именно в таком виде, где порядок значений внутренних массивов отличается.
Предположим, что мы хотим узнать существование массива [3,2,1] в основном массиве. Есть ли похожий? Как это сделать?
Давайте с «подводных камней». Как вам такое условие?
Для кого-то это будет шок! Это значит, что мы не можем сравнивать массивы «в тупую» как они есть. Что делать?
Нужно привести массивы к строке и сравнивать строки. Поможет нам в этом конструктор JSON и его метод stringify(). В чём его слабость? В том, что он НЕ сортирует ключи перед упаковкой в строку. 🙂
Смотрим как это работает:
JSON.stringify([3,2,1]) "[3,2,1]" JSON.stringify([1,2,3]) "[1,2,3]" JSON.stringify([3,2,1]) == JSON.stringify([1,2,3]) false
Итоговые строки не равны — false
Сейчас нам нужна функция, которая умеет сортировать значения в массивах, до их передачи в stringify()
Мы можем сделать так:
- Создаём новый набор Set
- Конвертируем набор в массив
- Сортируем массив
- Переводим массив в строку
- Сравниваем строки
Поехали
JSON.stringify(Array.from(new Set([3,2,1])).sort()) "[1,2,3]" JSON.stringify(Array.from(new Set([1,2,3])).sort()) "[1,2,3]" JSON.stringify(Array.from(new Set([3,2,1])).sort()) == JSON.stringify(Array.from(new Set([1,2,3])).sort()) true
Мы научились сравнивать массивы и теперь можем вернуться к нашему основному массиву и проверить есть ли в нём такой массив или нет.
Будем использовать тот же самый filter()
massiv.filter(i=>JSON.stringify(Array.from(new Set(i)).sort()) == JSON.stringify(Array.from(new Set([3,2,1])).sort()) )
В таком виде команда может не сработать, т. к. попытка приведения неитерируемого объекта к Набору вызовет ошибку:
new Set(4) — вызовет ошибку. По этой причине нам нужна двойная фильтрация!
Итог
Первая фильтрация:
massiv.filter(i => i.constructor.name == "Array")
Отловили все массивы в основном массиве
Вторая фильтрация:
massiv.filter( i=>i.constructor.name == "Array" ).filter( i => JSON.stringify(Array.from(new Set(i)).sort()) == JSON.stringify(Array.from(new Set([3,2,1])).sort()) )
Отыскали похожий массив на наш. Даже два похожих. Хочу обратить ваше внимание, что массив [1,2,3,4] был исключён из фильтрации по причине наличия в нём значения «4».
Длина отфильтрованного массива больше «0» (нуля), значит искомый массив встречается в основном массиве. Это и есть условие:
massiv.filter( i=>i.constructor.name == "Array" ).filter( i => JSON.stringify(Array.from(new Set(i)).sort()) == JSON.stringify(Array.from(new Set([3, 2, 1])).sort()) ).length > 0
true
Задача выполнена!
Информационные ссылки
JavaScript | Равенство объектов
JavaScript | Как узнать экземпляром какого класса является объект?
Стандарт ECMAScript — Раздел «25.5 The JSON Object» — https://tc39.es/ecma262/#sec-json-object