Перевірте, чи містить масив будь-який елемент іншого масиву в JavaScript


406

У мене є цільовий масив ["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
@ Алекс вибирай чувак-відповідь, грубо залишати відповідь без перевірки, особливо при цьому багато хороших відповідей. Я б вибрав підкреслення / подачу, якби я був ти.
Леон Габан

1
@LeonGaban Я не згоден. Я б не імпортував бібліотеку лише для виконання цієї операції.
devpato

2
@devpato так, що я передумав, рішення ES6 - це мій
фаворит

Як щодо arr1.some (el1 => arr2.includes (el1)); ?
Валашка

Відповіді:


551

Ваніль 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(..)обидва повертають істину, якщо даний аргумент присутній у масиві.


48
Цей код потребує пояснення - це може працювати, але немає значення, тому що ніхто нічого не
вчить

6
Array.prototype.some () перевіряє кожен елемент масиву на предмет тестової функції та повертає, trueякщо якийсь елемент масиву передає тестову функцію. В іншому випадку вона повертається false.
Хендека

2
це має бути правильна відповідь! Чудовий, який використовує ES6
Nacho

1
Чи очікується, що мій результат [false, false, false]замість порожнього масиву []?
Бетмен

@Batman: Результат істинний / хибний, але ви можете адаптувати рішення від 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 завжди потрібно використовувати замість ванільного 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)

DEMO: https://jsfiddle.net/r257wuv5/

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


1
це найпростіше і декларативне рішення
стридер

Я знаю, що я дуже спізнююсь на це, але перевірити консоль, якщо 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;
}());

DEMO: http://jsfiddle.net/u8Bzt/

У цьому випадку функцію можна змінити, щоб вона targetArrayбула передана як аргумент замість жорсткого кодування в закритті.


ОНОВЛЕННЯ2:

Хоча моє рішення вище може працювати і бути (сподіваюсь, більше) читабельним, я вважаю, що "кращим" способом впоратися з описаною нами концепцією є зробити щось трохи інакше. «Проблема» вищезгаданого рішення полягає в тому, що 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;
};

DEMO: http://jsfiddle.net/5Lv9v/

Мінус цього рішення полягає в тому, що можна використовувати (правильно) лише числа і рядки (і булеві), оскільки значення (неявно) перетворюються на рядки і встановлюються як ключі до карти пошуку. Це не зовсім добре / можливо / легко зробити для нелітеральних значень.


2
Відмінний приклад, другий приклад - просто геніальний.
Сорін Гайдау

Чому ви використовуєте для циклів, тоді як ви могли використовувати деякі або findIndex?
Це я ... Алекс

1
"some" значно спрощує код. Також будь-якийMatchInArray ([1,2,3, "коти", "4"], ["1", 4]) був би правдивим. Нарешті, це може бути ефективнішим, якщо у вас була велика кількість пошукових запитів та кешована цільова карта. Незважаючи на це, можливо, може бути підвищення продуктивності. Наприклад, я б здогадався, що "знайдений = toMatch [i]! == undefined" був би більш ефективним, а в деяких випадках кращим (щоб ви не оцінювали "" або 0 неправдиво)
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 ??
Анзіл хаН

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

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

// друкує FALSE

Сподіваюся, що допоможе комусь у майбутньому!


Мені дуже сподобалася друга частина запитання, щоб він працював для 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 (), щоб перевірити ефективність того чи іншого рішення. У своїх рішеннях я також перевіряю, який масив є найбільшим, а який - найменшим, щоб зробити операції більш ефективними.

3-е рішення може бути не найсильнішим, але ефективним. Я вирішив додати його, оскільки в деяких інтерв'ю з кодуванням вам заборонено використовувати вбудовані методи.

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

Примітка:

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

indexOf () vs включає ()

У кого кращі показники ? 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;
    };

ВИКОРИСТАННЯ. 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.includes (), які роблять свою роботу досить ефективно також

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;


Чи є спосіб отримати індекс включає елементи з originalFruits ??
Анзіл хаН

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

DEMO на 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. Використання підкреслювачів заради цього означає, що ваше рішення щільно поєднується з кодом сторонньої сторони.
csharpforevermore

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

2

Додавання до прототипу масиву

Відмова: Багато хто настійно не радить цього. Єдиний раз, коли б це було справді проблемою, було б, якщо бібліотека додала функцію прототипу з тим самим іменем (яка поводилася по-різному) або щось подібне.

Код:

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 відповідь, використовуйте includesнакреслений indexOfдля більш читабельного

нехай знайдено = arr1.some (r => arr2.indexOf (r)> = 0)
нехай знайдено = arr1.some (r => arr2.cludes (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.