Проверьте, содержит ли массив какой-либо элемент другого массива в JavaScript


407

У меня есть целевой массив ["apple","banana","orange"], и я хочу проверить, содержат ли другие массивы какой-либо из элементов целевого массива.

Например:

["apple","grape"] //returns true;

["apple","banana","pineapple"] //returns true;

["grape", "pineapple"] //returns false;

Как я могу сделать это в JavaScript?


3
Используйте forцикл и переберите целевой массив. Если каждый элемент содержится в текущем массиве (используйте current.indexOf(elem) !== -1), тогда они все там.
Blender

21
@ Алекс выбирает ответ, чувак, грубо оставлять ответ без контроля, особенно с таким количеством хороших ответов. Я бы выбрал подчеркивание / lodash, если бы я был тобой.
Леон Габан

1
@ LeonGaban Я не согласен. Я бы не стал импортировать библиотеку только для выполнения этой операции.
Девпато

2
@devpato да, передумал, решение ES6 - моя любимая
Леон Габан

Что насчет arr1.some (el1 => arr2.include (el1)); ?
Валашка

Ответы:


553

Ваниль JS

ES2016:

const found = arr1.some(r=> arr2.includes(r))

ES6:

const found = arr1.some(r=> arr2.indexOf(r) >= 0)

Как это работает

some(..)проверяет каждый элемент массива на соответствие тестовой функции и возвращает true, если какой-либо элемент массива проходит тестовую функцию, в противном случае возвращается false. indexOf(..) >= 0и includes(..)оба возвращают true, если данный аргумент присутствует в массиве.


48
Этот код нуждается в объяснении - это может сработать, но в этом нет никакой ценности, потому что никто ничего не изучает
TolMera

6
Array.prototype.some () проверяет каждый элемент массива на соответствие тестовой функции и возвращает, trueесли какой-либо элемент массива прошел тестовую функцию. В противном случае он возвращается false.
Хендека

2
это должен быть правильный ответ! Отличный, использующий ES6
Начо

1
Ожидается ли, что мой результат [false, false, false]вместо пустого массива []?
Бэтмен

@Batman: Результат true / false, но вы можете адаптировать решение от Mr. Skyisred
0zkr PM

230

Ваниль JS

/**
 * @description determine if an array contains one or more items from another array.
 * @param {array} haystack the array to search.
 * @param {array} arr the array providing items to check for in the haystack.
 * @return {boolean} true|false if haystack contains at least one item from arr.
 */
var findOne = function (haystack, arr) {
    return arr.some(function (v) {
        return haystack.indexOf(v) >= 0;
    });
};

10
Отличное решение! some()это рад. Выходит, как только что-то совпадает.
averydev

6
Событие более аккуратное, как это:arr.some(v=> haystack.indexOf(v) >= 0)
Пол Гримшоу

85
Также доступно в ES2016arr.some(v => haystack.includes(v))
loganfsmyth

5
в одну строку arr1.some(v => arr2.indexOf(v) >= 0).
webjay

1
На данный момент, возможно, было бы лучше избегать использования includes, поскольку, по-видимому, это не поддерживается в IE: stackoverflow.com/questions/36574351/…
Shafique Jamal

72

Если вы не против использования библиотеки, http://underscorejs.org/ имеет метод пересечения, который может упростить это:

var _ = require('underscore');

var target = [ 'apple', 'orange', 'banana'];
var fruit2 = [ 'apple', 'orange', 'mango'];
var fruit3 = [ 'mango', 'lemon', 'pineapple'];
var fruit4 = [ 'orange', 'lemon', 'grapes'];

console.log(_.intersection(target, fruit2)); //returns [apple, orange]
console.log(_.intersection(target, fruit3)); //returns []
console.log(_.intersection(target, fruit4)); //returns [orange]

Функция пересечения вернет новый массив с элементами, которым он соответствует, и, если не соответствует, возвращает пустой массив.


3
Я использовал это несколько раз, но учтите, что вопрос был в том, чтобы проверить, существует ли какой-либо элемент в другом массиве, чтобы не производить полное пересечение. С точки зрения производительности, если массивы большие, разница огромна, так как в первом случае вы можете выручить, как только найдете одно совпадение.
JHH

1
lodash гораздо более читабелен, чем ванильный Javascript, поэтому всегда следует использовать библиотеки вроде Ramda, а не vanilla imho. Лучше для всех разработчиков ...
Леон Габан

52

ES6 (самый быстрый)

const a = ['a', 'b', 'c'];
const b = ['c', 'a', 'd'];
a.some(v=> b.indexOf(v) !== -1)

ES2016

const a = ['a', 'b', 'c'];
const b = ['c', 'a', 'd'];
a.some(v => b.includes(v));

Подчеркивать

const a = ['a', 'b', 'c'];
const b = ['c', 'a', 'd'];
_.intersection(a, b)

ДЕМО: https://jsfiddle.net/r257wuv5/

jsPerf: https://jsperf.com/array-contains-any-element-of-another-array


1
это самый простой и декларативное решение
Strider

Я знаю, что действительно опаздываю на это, но чтобы проверить консоль, добавит ли JSFiddle JQuery Edge и включит Firebug Lite
Rojo

JSperf ссылка не работает
DarckBlezzer

Есть ли разница во времени и сложности пространства? Что будет лучшим решением в отношении сложности?
Начо

41

Если вам не нужно приведение типов (из-за использования indexOf), вы можете попробовать что-то вроде следующего:

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

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

Конечно, вы можете поменять числа для всего, что вы хотите использовать - строки в порядке, как ваш пример.

И в моем конкретном примере, результат должен быть, trueпотому что второй массив 3существует в цели.


ОБНОВИТЬ:

Вот как я могу организовать это в функцию (с некоторыми незначительными изменениями по сравнению с предыдущим):

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

ДЕМО: http://jsfiddle.net/u8Bzt/

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


UPDATE2:

Хотя мое решение, приведенное выше, может работать и (возможно, более) читабельно, я считаю, что «лучший» способ справиться с описанной мной концепцией - сделать что-то немного по-другому. «Проблема» с вышеприведенным решением состоит в том, что indexOfвнутри цикла происходит полное зацикливание целевого массива для каждого элемента в другом массиве. Это можно легко «исправить» с помощью «поиска» (карта ... литерал объекта JavaScript). Это позволяет использовать два простых цикла для каждого массива. Вот пример:

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

ДЕМО: http://jsfiddle.net/5Lv9v/

Недостатком этого решения является то, что только числа и строки (и логические значения) могут использоваться (правильно), поскольку значения (неявно) преобразуются в строки и задаются в качестве ключей для карты поиска. Это не совсем хорошо / возможно / легко сделать для не буквальных значений.


2
Отличный пример, второй пример просто блестящий.
Сорин Хайдау

Почему вы используете для циклов, а вы можете использовать некоторые или findIndex?
Это я ... Алекс

1
«Некоторые» значительно упрощают код. Кроме того, anyMatchInArray ([1,2,3, "cats", "4"], ["1", 4]) будет истинным. Наконец, это может быть более производительным, если у вас было большое количество поисков и вы кэшировали targetMap. Даже в этом случае, возможно, может быть повышение производительности. Например, я бы предположил, что "found = toMatch [i]! == undefined" будет более производительным, а в некоторых случаях лучше (так что вы не оцениваете "" или 0 в false)
csga5000

«иначе он вернется undefined... вот для чего !!» - это неправильно. Это вернет булеву оппозицию !.
AlienWebguy

41

Решение ES6:

let arr1 = [1, 2, 3];
let arr2 = [2, 3];

let isFounded = arr1.some( ai => arr2.includes(ai) );

В отличие от этого: должен содержать все значения.

let allFounded = arr2.every( ai => arr1.includes(ai) );

Надеюсь, будет полезно.


Есть ли способ , чтобы получить индекс arra2 значений от array1 ??
Anzil khaN

1
В этом случае мы можем использовать «фильтр» вместо «некоторые». Затем он будет возвращать массив вместо логического значения, и вы сможете легко получить доступ к значению оттуда.
tanvir993

29

Вы можете использовать lodash и сделать:

_.intersection(originalTarget, arrayToCheck).length > 0

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


Это не является оптимальным с точки зрения производительности, поскольку для этой задачи достаточно найти первое совпадение, хотя intersectionбудет продолжать сравнивать даже после нахождения первого совпадения, чтобы найти все из них. Это все равно что использовать filterкогда нужно просто find.
Александр

29

Использование фильтра / indexOf :

function containsAny(source,target)
{
    var result = source.filter(function(item){ return target.indexOf(item) > -1});   
    return (result.length > 0);  
}    


//results

var fruits = ["apple","banana","orange"];


console.log(containsAny(fruits,["apple","grape"]));

console.log(containsAny(fruits,["apple","banana","pineapple"]));

console.log(containsAny(fruits,["grape", "pineapple"]));


Это страдает от той же проблемы, что и библиотечные функции, такие как _.intersection, в том, что она будет продолжать поиск совпадений даже после нахождения. Для небольших массивов это, очевидно, не имеет значения.
JHH

13
const areCommonElements = (arr1, arr2) => {
    const arr2Set = new Set(arr2);
    return arr1.some(el => arr2Set.has(el));
};

Или вы можете даже добиться лучшей производительности, если сначала узнаете, какой из этих двух массивов длиннее, и выделите Setдля самого длинного массива, а someметод для самого короткого:

const areCommonElements = (arr1, arr2) => {
    const [shortArr, longArr] = (arr1.length < arr2.length) ? [arr1, arr2] : [arr2, arr1];
    const longArrSet = new Set(longArr);
    return shortArr.some(el => longArrSet.has(el));
};

3
В то время как люди продолжают публиковать решения с вложением indexOfи includes, вы первыми отвечаете более эффективным решением на основе множеств, используя нативное Set, спустя 4 года после его введения в EcmaScript. +1
трико

9

Я нашел этот короткий и приятный синтаксис для соответствия всех или некоторых элементов между двумя массивами. Например

// ИЛИ операция. найти, существует ли какой-либо из элементов array2 в array1. Он вернется, как только будет найдено первое совпадение, так как некоторые методы прерываются, когда функция возвращает TRUE.

let array1 = ['a', 'b', 'c', 'd', 'e'], array2 = ['a', 'b'];

console.log(array2.some(ele => array1.includes(ele)));

// печатает ИСТИНА

// И операция. найти, если все элементы array2 существуют в array1. Это вернется, как только не будет первого совпадения, так как какой-то метод прерывается, когда функция возвращает TRUE

let array1 = ['a', 'b', 'c', 'd', 'e'], array2 = ['a', 'x'];

console.log(!array2.some(ele => !array1.includes(ele)));

// печатает ЛОЖЬ

Надеюсь, что это поможет кому-то в будущем!


Мне очень понравилась вторая часть вопроса, чтобы заставить ее работать на ES5, она делалась так:! Array2.some (function (ele) {return array1.indexOf (ele) === -1});
Friesgaard

6

Вы можете использовать вложенный вызов Array.prototype.some. Преимущество заключается в том, что он будет освобожден при первом совпадении вместо других решений, которые будут проходить через полный вложенный цикл.

например.

var arr = [1, 2, 3];
var match = [2, 4];

var hasMatch = arr.some(a => match.some(m => a === m));

4

Вот интересный случай, которым я поделился.

Допустим, у вас есть массив объектов и массив выбранных фильтров.

let arr = [
  { id: 'x', tags: ['foo'] },
  { id: 'y', tags: ['foo', 'bar'] },
  { id: 'z', tags: ['baz'] }
];

const filters = ['foo'];

Чтобы применить выбранные фильтры к этой структуре, мы можем

if (filters.length > 0)
  arr = arr.filter(obj =>
    obj.tags.some(tag => filters.includes(tag))
  );

// [
//   { id: 'x', tags: ['foo'] },
//   { id: 'y', tags: ['foo', 'bar'] }
// ]

4

Я написал 3 решения. По сути, они делают то же самое. Они возвращают истину, как только получают true. Я написал 3 решения только для того, чтобы показать 3 разных способа сделать что-то. Теперь это зависит от того, что вам нравится больше. Вы можете использовать performance.now () для проверки производительности одного или другого решения. В своих решениях я также проверяю, какой массив является самым большим, а какой - самым маленьким, чтобы сделать операции более эффективными.

Третье решение, возможно, не самое симпатичное, но эффективное. Я решил добавить его, потому что в некоторых интервью по кодированию вам не разрешено использовать встроенные методы.

Наконец, конечно ... мы можем придумать решение с 2 NESTED для циклов (методом грубой силы), но вы хотите этого избежать, потому что сложность времени плохая O (n ^ 2) .

Замечания:

вместо того, чтобы использовать, .includes()как некоторые другие люди, вы можете использовать .indexOf(). если вы просто проверьте, больше ли значение, чем 0. Если значение не существует, вы получите -1. если он существует, он даст вам больше 0.

indexOf () против включает в себя ()

Какой из них имеет лучшую производительность ? indexOf()немного, но включает в себя более читабельным, на мой взгляд.

Если я не ошибаюсь .includes()и indexOf()использую петли за сценой, то вы будете в точке O (n ^ 2) при использовании их с .some().

ИСПОЛЬЗОВАНИЕ петли

 const compareArraysWithIncludes = (arr1, arr2) => {
     const [smallArray, bigArray] =
        arr1.length < arr2.length ? [arr1, arr2] : [arr2, arr1];

     for (let i = 0; i < smallArray.length; i++) {
       return bigArray.includes(smallArray[i]);
     }

      return false;
    };

USING .some ()

const compareArraysWithSome = (arr1, arr2) => {
  const [smallArray, bigArray] =
    arr1.length < arr2.length ? [arr1, arr2] : [arr2, arr1];
  return smallArray.some(c => bigArray.includes(c));
};

ИСПОЛЬЗОВАНИЕ КАРТ Временная сложность O (2n) => O (n)

const compararArraysUsingObjs = (arr1, arr2) => {
  const map = {};

  const [smallArray, bigArray] =
    arr1.length < arr2.length ? [arr1, arr2] : [arr2, arr1];

  for (let i = 0; i < smallArray.length; i++) {
    if (!map[smallArray[i]]) {
      map[smallArray[i]] = true;
    }
  }

  for (let i = 0; i < bigArray.length; i++) {
    if (map[bigArray[i]]) {
      return true;
    }
  }

  return false;
};

Код по моему: stackblitz

Я не эксперт по производительности и не BigO, так что если что-то, что я сказал неправильно, дайте мне знать.


3

Как насчет использования комбинации некоторых / findIndex и indexOf?

Так что-то вроде этого:

var array1 = ["apple","banana","orange"];
var array2 = ["grape", "pineapple"];

var found = array1.some(function(v) { return array2.indexOf(v) != -1; });

Чтобы сделать его более читабельным, вы можете добавить эту функцию к самому объекту Array.

Array.prototype.indexOfAny = function (array) {
    return this.findIndex(function(v) { return array.indexOf(v) != -1; });
}

Array.prototype.containsAny = function (array) {
    return this.indexOfAny(array) != -1;
}

Примечание. Если вы хотите что-то сделать с предикатом, вы можете заменить внутренний indexOf другим findIndex и предикатом.


3

Мое решение использует помощники массивов Array.prototype.some () и Array.prototype.include (), которые также выполняют свою работу довольно эффективно.

ES6

const originalFruits = ["apple","banana","orange"];

const fruits1 = ["apple","banana","pineapple"];

const fruits2 = ["grape", "pineapple"];

const commonFruits = (myFruitsArr, otherFruitsArr) => {
  return myFruitsArr.some(fruit => otherFruitsArr.includes(fruit))
}
console.log(commonFruits(originalFruits, fruits1)) //returns true;
console.log(commonFruits(originalFruits, fruits2)) //returns false;


Есть ли способ получить индекс включаемых элементов из оригинальных фруктов ??
Anzil khaN

3

Еще одно решение

var a1 = [1, 2, 3, 4, 5]
var a2 = [2, 4]

Проверьте, содержат ли a1 все элементы a2

var result = a1.filter(e => a2.indexOf(e) !== -1).length === a2.length
console.log(result)

2

Это можно сделать, просто выполнив итерации по основному массиву и проверив, содержит ли другой массив какой-либо целевой элемент или нет.

Попробуй это:

function Check(A) {
    var myarr = ["apple", "banana", "orange"];
    var i, j;
    var totalmatches = 0;
    for (i = 0; i < myarr.length; i++) {
        for (j = 0; j < A.length; ++j) {
            if (myarr[i] == A[j]) {

                totalmatches++;

            }

        }
    }
    if (totalmatches > 0) {
        return true;
    } else {
        return false;
    }
}
var fruits1 = new Array("apple", "grape");
alert(Check(fruits1));

var fruits2 = new Array("apple", "banana", "pineapple");
alert(Check(fruits2));

var fruits3 = new Array("grape", "pineapple");
alert(Check(fruits3));

ДЕМО в JSFIDDLE


2

С подчеркиваниями

var a1 = [1,2,3];
var a2 = [1,2];

_.every(a1, function(e){ return _.include(a2, e); } ); //=> false
_.every(a2, function(e){ return _.include(a1, e); } ); //=> true

2
Лично, хотя мне нравятся подчеркивания, это классический пример того, как может выглядеть сложный код. Мало того, что это трудно понять как код подчеркивания, но и с точки зрения общего кодирования, это также верно (например, слово «каждый» не приходит на ум, когда я хочу найти индекс чего-то в массиве, но "indexOf" делает). Мы должны избегать ненужного использования сторонних инструментов, когда для нескольких дополнительных символов может быть предоставлено чистое решение JavaScript. Использование underscorejs ради этого означает, что ваше решение тесно связано со сторонним кодом.
csharpforevermore

@csharpforevermore Я полагаю, это дело вкуса, вы говорите, что это решение более замысловатое, чем все остальные, использующие, indexOfя думаю, обратное :). С другой стороны, я согласен с тем, что стараюсь не добавлять внешние библиотеки, если они на самом деле не нужны, но я не особо одержим этим, библиотеки третьих сторон предлагают не только полезные, но и надежные функции. Например: проверяли ли вы все крайние случаи и браузеры мэров с вашим решением? .. (кстати, everyне пытается найти индекс в списке, а оценивает что-то в каждом элементе списка)
fguillen

2

Добавление в Array Prototype

Отказ от ответственности: многие настоятельно рекомендуют против этого. Единственный раз, когда это действительно было бы проблемой, было, если бы библиотека добавила функцию-прототип с тем же именем (которая ведет себя по-разному) или что-то в этом роде.

Код:

Array.prototype.containsAny = function(arr) {
    return this.some(
        (v) => (arr.indexOf(v) >= 0)
    )
}

Без использования функций большой стрелки:

Array.prototype.containsAny = function(arr) {
    return this.some(function (v) {
        return arr.indexOf(v) >= 0
    })
}

Применение

var a = ["a","b"]

console.log(a.containsAny(["b","z"]))    // Outputs true

console.log(a.containsAny(["z"]))    // Outputs false

2

Vanilla JS с частичным совпадением и без учета регистра

Проблема с некоторыми предыдущими подходами состоит в том, что они требуют точного соответствия каждого слова . Но что делать, если вы хотите предоставить результаты для частичных совпадений?

function search(arrayToSearch, wordsToSearch) {
    arrayToSearch.filter(v => 
        wordsToSearch.every(w => 
            v.toLowerCase().split(" ").
                reduce((isIn, h) => isIn || String(h).indexOf(w) >= 0, false)
            )
        )
}
//Usage
var myArray = ["Attach tag", "Attaching tags", "Blah blah blah"];
var searchText = "Tag attach";
var searchArr = searchText.toLowerCase().split(" "); //["tag", "attach"]

var matches = search(myArray, searchArr);
//Will return
//["Attach tag", "Attaching tags"]

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


2

Обновите ответ @Paul Grimshaw, используйте includesinsteed of indexOfдля более читабельного

let found = arr1.some (r => arr2.indexOf (r)> = 0)
let found = arr1.some (r => arr2.include (r))


1

Я придумал решение в узле с использованием подчеркивания js вот так:

var checkRole = _.intersection(['A','B'], ['A','B','C']);
if(!_.isEmpty(checkRole)) { 
     next();
}

0

Лично я бы использовал следующую функцию:

var arrayContains = function(array, toMatch) {
    var arrayAsString = array.toString();
    return (arrayAsString.indexOf(','+toMatch+',') >-1);
}

Метод toString () всегда будет использовать запятые для разделения значений. Будет действительно работать только с примитивными типами.


2
Это не будет работать, когда элементы находятся в начале или в конце массива, или в другом порядке.
DanielM

1
-1 потому что, как сказал DanielM, это сломано. Вы можете добавить и добавить запятую к arrayAsString в качестве обходного пути, но, честно говоря, это просто кажется слишком сложным решением для использования строк.
JHH

0

Массив .filter () с вложенным вызовом .find () вернет все элементы в первом массиве, которые являются членами второго массива. Проверьте длину возвращенного массива, чтобы определить, был ли какой-либо из второго массива в первом массиве.

getCommonItems(firstArray, secondArray) {
  return firstArray.filter((firstArrayItem) => {
    return secondArray.find((secondArrayItem) => {
      return firstArrayItem === secondArrayItem;
    });
  });
}

Есть ли способ «очистить» массив? Как удалить значения во втором массиве, если они существуют в первом?
Sandrooco

0
console.log("searching Array: "+finding_array);
console.log("searching in:"+reference_array);
var check_match_counter = 0;
for (var j = finding_array.length - 1; j >= 0; j--) 
{
    if(reference_array.indexOf(finding_array[j]) > 0)
    {
        check_match_counter = check_match_counter + 1;
    }
}
 var match = (check_match_counter > 0) ? true : false;
console.log("Final result:"+match);

0
var target = ["apple","banana","orange"];
var checkArray = ["apple","banana","pineapple"];

var containsOneCommonItem = target.some(x => checkArray.some(y => y === x));`

["apple","grape"] //returns true;

["apple","banana","pineapple"] //returns true;

["grape", "pineapple"] //returns false;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.