Controlla se un array contiene elementi di un altro array in JavaScript


406

Ho un array di destinazione ["apple","banana","orange"]e voglio verificare se altri array contengono uno degli elementi dell'array di destinazione.

Per esempio:

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

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

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

Come posso farlo in JavaScript?


3
Utilizzare un forciclo e scorrere sull'array di destinazione. Se ogni elemento è contenuto nell'array corrente (usa current.indexOf(elem) !== -1), allora sono tutti lì dentro.
Blender

21
@Alex scegli una risposta amico, è scortese lasciare una risposta deselezionata, specialmente con queste tante buone risposte. Sceglierei il trattino basso / lodash se fossi in te.
Leon Gaban,

1
@LeonGaban Non sono d'accordo. Non importerei una libreria solo per eseguire questa operazione.
Devpato

2
@devpato sì, ho cambiato idea, la soluzione ES6 è la mia preferita
Leon Gaban,

Che dire di arr1.some (el1 => arr2.includes (el1)); ?
Walaszka,

Risposte:


551

Vanilla JS

ES2016:

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

ES6:

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

Come funziona

some(..)controlla ogni elemento dell'array rispetto a una funzione di test e restituisce true se qualsiasi elemento dell'array supera la funzione di test, altrimenti restituisce false. indexOf(..) >= 0ed includes(..)entrambi restituiscono vero se l'argomento dato è presente nella matrice.


48
Questo codice ha bisogno di una spiegazione - potrebbe funzionare, ma non ha valore perché nessuno sta imparando nulla
TolMera,

6
Array.prototype.some () controlla ogni elemento dell'array rispetto a una funzione di test e restituisce truese qualsiasi elemento dell'array supera la funzione di test. Altrimenti, ritorna false.
Hendeca,

2
questa dovrebbe essere la risposta corretta! Grande usando ES6
Nacho il

1
Si prevede che il mio risultato sia [false, false, false]invece di un array vuoto []?
Batman,

@Batman: il risultato è vero / falso, ma è possibile adattare la soluzione di Mr. skyisred
0zkr PM

230

vaniglia 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
Bella soluzione! some()è rad. Si chiude non appena qualcosa corrisponde.
averydev,

6
un ordine così ordinato:arr.some(v=> haystack.indexOf(v) >= 0)
Paul Grimshaw,

85
Disponibile anche in ES2016arr.some(v => haystack.includes(v))
loganfsmyth,

5
in una riga arr1.some(v => arr2.indexOf(v) >= 0).
webjay

1
Per il momento, potrebbe essere meglio evitare di usare includes, come a quanto pare non è supportato in IE: stackoverflow.com/questions/36574351/...
Shafique Jamal

72

Se non sei contrario all'utilizzo di un libray, http://underscorejs.org/ ha un metodo di intersezione, che può semplificare questo:

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]

La funzione di intersezione restituirà un nuovo array con gli elementi corrispondenti e, in caso contrario, restituirà un array vuoto.


3
Ho usato più volte, ma è da notare che la domanda riguardava la verifica se ogni elemento esiste dell'altra schiera, di non produrre l'intera intersezione. In termini di prestazioni c'è una grande differenza se le matrici sono grandi poiché nel primo caso è possibile eseguire il salvataggio non appena si trova una corrispondenza.
JHH,

1
lodash è molto più leggibile di Javascript vaniglia, le librerie simili a Ramda dovrebbero sempre essere usate al posto di imho vaniglia. Meglio per tutti gli sviluppatori ...
Leon Gaban,

52

ES6 (più veloce)

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

Sottolineare

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
questa è la soluzione più semplice e dichiarativa
strider

So che sono davvero in ritardo per questo, ma per controllare la console se JSFiddle aggiunge JQuery Edge e attiva Firebug Lite
Rojo,

Collegamento JSperf interrotto
DarckBlezzer,

C'è qualche differenza nella complessità del tempo e dello spazio? Quale sarebbe la migliore soluzione per quanto riguarda la complessità?
nacho

41

Se non hai bisogno della coercizione del tipo (a causa dell'uso di indexOf), puoi provare qualcosa del tipo seguente:

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

Dove arrcontiene gli articoli di destinazione. Alla fine, foundmostrerà se il secondo array ha avuto almeno una partita contro il bersaglio.

Ovviamente, puoi scambiare i numeri con tutto ciò che vuoi usare: le stringhe vanno bene, come nel tuo esempio.

E nel mio esempio specifico, il risultato dovrebbe essere trueperché il secondo array 3esiste nella destinazione.


AGGIORNARE:

Ecco come lo organizzerei in una funzione (con alcune piccole modifiche rispetto a prima):

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 questo caso, la funzione potrebbe essere modificata per targetArrayessere passata come argomento anziché codificata nella chiusura.


UPDATE2:

Mentre la mia soluzione sopra potrebbe funzionare ed essere (si spera più) leggibile, credo che il modo "migliore" di gestire il concetto che ho descritto sia quello di fare qualcosa in modo leggermente diverso. Il "problema" con la soluzione di cui sopra è che l' indexOfinterno del ciclo fa sì che l'array di destinazione venga completamente riavvolto per ogni elemento nell'altro array. Questo può essere facilmente "risolto" utilizzando una "ricerca" (una mappa ... un oggetto JavaScript letterale). Ciò consente due semplici loop, su ciascun array. Ecco un esempio:

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/

L'aspetto negativo di questa soluzione è che solo i numeri e le stringhe (e i booleani) possono essere utilizzati (correttamente), poiché i valori vengono (implicitamente) convertiti in stringhe e impostati come chiavi per la mappa di ricerca. Questo non è esattamente buono / possibile / facilmente eseguibile per valori non letterali.


2
Esempio eccellente, il secondo esempio è semplicemente geniale.
Sorin Haidau,

Perché stai usando i loop mentre potresti usarne alcuni o findIndex?
Sono io ... Alex,

1
"some" semplifica notevolmente il codice. Inoltre, anyMatchInArray ([1,2,3, "cats", "4"], ["1", 4]) sarebbe vero. Infine, questo potrebbe essere più efficace se hai avuto un gran numero di ricerche e memorizzato nella cache la mappa target. Anche così, potrebbero esserci probabilmente aumenti delle prestazioni. Ad esempio, immagino che "found = toMatch [i]! == undefined" sarebbe più performante, e in alcuni casi migliore (in modo da non valutare "" o 0 da falso)
csga5000,

"altrimenti tornerà undefined... ecco a cosa !!serve" - ​​è sbagliato. Restituirà l'opposizione booleana di !.
AlienWebguy,

41

Soluzione ES6:

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

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

A differenza di esso: Must contiene tutti i valori.

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

Spero, sarà utile.


C'è un modo per ottenere l'indice dei valori di arra2 da array1 ??
Anzil khaN

1
In tal caso, possiamo usare "filter" anziché "some". Quindi restituirà un array anziché un valore booleano e puoi facilmente accedere al valore da lì.
tanvir993,

29

Puoi usare lodash e fare:

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

L'impostazione dell'intersezione viene eseguita su entrambe le raccolte producendo una matrice di elementi identici.


Non è ottimale in termini di prestazioni, poiché per questo problema è sufficiente trovare la prima partita, mentre intersectioncontinuerà a confrontare anche dopo aver trovato la prima partita per trovarle tutte. È come usare filterquando ne hai bisogno find.
Alexander

29

Utilizzando filter / 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"]));


Questo soffre dello stesso problema delle funzioni di libreria come _.intersection in quanto continuerà a cercare corrispondenze anche dopo averne trovato uno. Per i piccoli array ovviamente non importa però.
JHH,

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

Oppure puoi anche ottenere prestazioni migliori se scopri per primo quale di questi due array è più lungo e se ne ricava Setl'array più lungo, applicando il somemetodo su quello più corto:

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
Mentre le persone continuano a pubblicare soluzioni con annidamento indexOfe includestu sei il primo a rispondere con la soluzione basata su set più efficiente, usando il nativo Set, 4 anni dopo che è stato introdotto in EcmaScript. +1
trincot

9

Ho trovato questa sintassi breve e dolce per abbinare tutti o alcuni elementi tra due array. Per esempio

// Operazione OR. trova se esiste uno degli elementi array2 in array1. Questo ritornerà non appena c'è una prima corrispondenza poiché alcuni metodi si interrompono quando la funzione restituisce VERO

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

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

// stampa VERO

// AND operazione. trova se tutti gli elementi array2 esistono in array1. Questo ritornerà non appena non c'è una prima corrispondenza poiché alcuni metodi si interrompono quando la funzione restituisce VERO

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

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

// stampa FALSO

Spero che aiuti qualcuno in futuro!


Mi è davvero piaciuta la seconda parte della domanda, per farla funzionare per ES5, mi è piaciuto:! Array2.some (function (ele) {return array1.indexOf (ele) === -1});
Friesgaard,

6

È possibile utilizzare una chiamata Array.prototype.some nidificata. Questo ha il vantaggio che verrà rilasciato alla prima partita invece di altre soluzioni che verranno eseguite attraverso il ciclo completo nidificato.

per esempio.

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

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

4

Ecco un caso interessante che ho pensato di condividere.

Supponiamo che tu abbia una matrice di oggetti e una matrice di filtri selezionati.

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

const filters = ['foo'];

Per applicare i filtri selezionati a questa struttura possiamo

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

Ho scritto 3 soluzioni. In sostanza fanno lo stesso. Tornano veri non appena arrivano true. Ho scritto le 3 soluzioni solo per mostrare 3 modi diversi di fare le cose. Ora dipende da cosa ti piace di più. È possibile utilizzare performance.now () per verificare le prestazioni di una soluzione o dell'altra. Nelle mie soluzioni sto anche controllando quale array è il più grande e quale è il più piccolo per rendere le operazioni più efficienti.

La terza soluzione potrebbe non essere la più carina ma è efficiente. Ho deciso di aggiungerlo perché in alcune interviste di codifica non è consentito utilizzare metodi integrati.

Infine, certo ... possiamo trovare una soluzione con 2 NESTED per i loop (la via della forza bruta) ma vuoi evitarlo perché la complessità temporale è scarsa O (n ^ 2) .

Nota:

invece di usare .includes()come altre persone, puoi usare .indexOf(). se lo fai, controlla se il valore è maggiore di 0. Se il valore non esiste, otterrai -1. se esiste, ti darà maggiore di 0.

indexOf () vs Includes ()

Quale ha prestazioni migliori ? indexOf()per un po ', ma include è più leggibile secondo me.

Se non sbaglio .includes()e indexOf()uso dei loop dietro la scena, allora sarai su O (n ^ 2) quando li usi .some().

USANDO il ciclo

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

USANDO .some ()

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

USO DELLE MAPPE Complessità temporale 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;
};

Codice nel mio: stackblitz

Non sono un esperto di prestazioni né BigO quindi se qualcosa che ho detto è sbagliato fammelo sapere.


3

Che ne dici di usare una combinazione di some / findIndex e indexOf?

Quindi qualcosa del genere:

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

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

Per renderlo più leggibile è possibile aggiungere questa funzionalità all'oggetto Array stesso.

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

Nota: se si desidera fare qualcosa con un predicato, è possibile sostituire l'indice interno di un altro findIndex e un predicato


3

La mia soluzione applica Array.prototype.some () e Array.prototype.includes () aiutanti di array che svolgono anche il loro lavoro in modo abbastanza efficiente

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;


C'è un modo per ottenere l'indice degli articoli inclusi da originalFruits ??
Anzil khaN

3

Solo un'altra soluzione

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

Controlla se a1 contiene tutti gli elementi di a2

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

2

Può essere fatto semplicemente ripetendo l'array principale e verificando se l'altro array contiene o meno l'elemento target.

Prova questo:

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 su JSFIDDLE


2

Con underscorejs

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
Personalmente, sebbene mi piaccia underscorejs, questo è un classico esempio di come può apparire il codice contorto. Non solo è difficile da comprendere come codice underscorejs, ma da un punto di vista di codifica generale, lo stesso vale anche (ad esempio la parola "ogni" non mi viene in mente quando voglio trovare l'indice di qualcosa in un array ma "indexOf" lo fa). Dovremmo evitare l'uso inutile di strumenti di terze parti quando per alcuni caratteri in più, potrebbe essere fornita una soluzione JavaScript pura. L'uso di underscorejs per il gusto di farlo significa che la tua soluzione diventa strettamente accoppiata con codice di terze parti.
csharpforevermore,

@csharpforevermore Suppongo che questa sia una questione di gusti, tu dici che questa soluzione è più contorta di tutte le altre che uso indexOfpenso il contrario :). D'altro canto, sono d'accordo nel cercare di non aggiungere librerie esterne se non sono realmente necessarie, ma non ne sono davvero ossessionato, le librerie di terze parti non offrono solo funzionalità utili ma anche funzionalità solide . Ad esempio: hai testato tutti i casi limite e i browser principali con la tua soluzione? .. (a proposito, everynon sta cercando di trovare un indice in un elenco ma sta valutando qualcosa in ogni elemento dell'elenco)
fguillen,

2

Aggiunta al prototipo di array

Disclaimer: molti sconsigliano fortemente questo. L'unica volta sarebbe davvero un problema se una libreria aggiungesse una funzione prototipo con lo stesso nome (che si comportava diversamente) o qualcosa del genere.

Codice:

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

Senza utilizzare le funzioni freccia grande:

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

uso

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

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

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

2

Vanilla JS con abbinamento parziale e case insensitive

Il problema con alcuni approcci precedenti è che richiedono una corrispondenza esatta di ogni parola . Ma cosa succede se si desidera fornire risultati per le partite parziali?

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"]

Ciò è utile quando si desidera fornire una casella di ricerca in cui gli utenti digitano parole e i risultati possono avere tali parole in qualsiasi ordine, posizione e caso.


2

Aggiorna @Paul Grimshaw rispondi, usa includesinsteed di indexOfper più leggibile

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


1

Ho trovato una soluzione nel nodo usando underscore js in questo modo:

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

0

Personalmente, vorrei usare la seguente funzione:

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

Il metodo "toString ()" utilizzerà sempre le virgole per separare i valori. Funzionerà davvero solo con tipi primitivi.


2
Questo non funzionerà quando gli elementi sono all'inizio o alla fine dell'array o in un ordine diverso.
DanielM,

1
-1 perché, come ha detto DanielM, questo è rotto. È possibile anteporre e aggiungere una virgola a arrayAsString come soluzione alternativa, ma onestamente sembra una soluzione troppo complicata per utilizzare le stringhe.
JHH,

0

Array .filter () con una chiamata nidificata a .find () restituirà tutti gli elementi nel primo array che sono membri del secondo array. Controllare la lunghezza dell'array restituito per determinare se uno qualsiasi del secondo array era nel primo array.

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

C'è un modo per "pulire" l'array? Come eliminare i valori nel secondo array se esistono nel primo?
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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.