Compruebe si una matriz contiene algún elemento de otra matriz en JavaScript

406

Tengo una matriz de destino ["apple","banana","orange"]y quiero verificar si otras matrices contienen alguno de los elementos de la matriz de destino.

Por ejemplo:

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

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

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

¿Cómo puedo hacerlo en JavaScript?

Alex
fuente
3
Use un forbucle e itere sobre la matriz de destino. Si cada elemento está contenido dentro de la matriz actual (uso current.indexOf(elem) !== -1), entonces están todos allí.
Blender
21
@Alex elige una respuesta amigo, es grosero dejar una respuesta sin marcar, especialmente con esta cantidad de buenas respuestas. Elegiría el guión bajo / lodash si fuera tú.
Leon Gaban
1
@LeonGaban No estoy de acuerdo. No importaría una biblioteca solo para realizar esta operación.
devpato
2
@devpato sí cambió de opinión, la solución ES6 es mi favorito
Leon Gaban
¿Qué pasa con arr1.some (el1 => arr2.includes (el1)); ?
Walaszka

Respuestas:

551

Vanilla JS

ES2016:

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

ES6:

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

Cómo funciona

some(..)comprueba cada elemento de la matriz con una función de prueba y devuelve verdadero si algún elemento de la matriz pasa la función de prueba, de lo contrario, devuelve falso. indexOf(..) >= 0y includes(..)ambos devuelven verdadero si el argumento dado está presente en la matriz.

Paul Grimshaw
fuente
48
Este código necesita una explicación; esto podría funcionar, pero no tiene valor porque nadie está aprendiendo nada
TolMera
66
Array.prototype.some () compara cada elemento de la matriz con una función de prueba y devuelve truesi algún elemento de la matriz pasa la función de prueba. De lo contrario, vuelve false.
Hendeca
2
¡Esta debería ser la respuesta correcta! Genial usando ES6
Nacho
1
¿Se espera que mi resultado sea en [false, false, false]lugar de una matriz vacía []?
Batman
@Batman: El resultado es verdadero / falso, pero puede adaptar la solución del Sr. skyisred
0zkr PM
230

vainilla 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;
    });
};
skyisred
fuente
10
Buena solución! some()es rad. Se cierra tan pronto como algo coincide.
averydev
66
evento más ordenado como este:arr.some(v=> haystack.indexOf(v) >= 0)
Paul Grimshaw
85
También disponible en ES2016arr.some(v => haystack.includes(v))
loganfsmyth
55
en una línea arr1.some(v => arr2.indexOf(v) >= 0).
webjay
1
Por ahora, podría ser mejor evitar el uso includes, ya que aparentemente no es compatible con IE: stackoverflow.com/questions/36574351/…
Shafique Jamal
72

Si no se opone a usar una biblioteca, http://underscorejs.org/ tiene un método de intersección, que puede simplificar esto:

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 función de intersección devolverá una nueva matriz con los elementos con los que coincide y, si no coincide, devuelve una matriz vacía.

willz
fuente
3
Lo he usado varias veces, pero tenga en cuenta que la pregunta era sobre verificar si existe algún elemento en la otra matriz, no para producir la intersección completa. En términos de rendimiento, hay una gran diferencia si las matrices son grandes, ya que en el primer caso puede rescatar tan pronto como encuentre una coincidencia.
JHH
1
lodash es mucho más legible que Javascript de vainilla, las librerías como esta en Ramda siempre deben usarse en lugar de vanilla imho. Mejor para todos los desarrolladores ...
Leon Gaban
52

ES6 (el más rápido)

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

Guion bajo

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

anochecer
fuente
1
esta es la solución más simple y declarativa
strider
Sé que realmente llego tarde a esto, pero para verificar la consola si JSFiddle agrega JQuery Edge y enciende Firebug Lite
Rojo
Enlace JSperf roto
DarckBlezzer
¿Hay alguna diferencia en el tiempo y la complejidad del espacio? ¿Cuál sería la mejor solución con respecto a la complejidad?
nacho
41

Si no necesita la coerción de tipo (debido al uso de indexOf), puede intentar algo como lo siguiente:

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

Donde arrcontiene los elementos de destino. Al final, foundse mostrará si la segunda matriz tenía al menos una coincidencia contra el objetivo.

Por supuesto, puede intercambiar números por cualquier cosa que desee usar: las cadenas están bien, como su ejemplo.

Y en mi ejemplo específico, el resultado debería ser trueporque la segunda matriz 3existe en el objetivo.


ACTUALIZAR:

Así es como lo organizaría en una función (con algunos cambios menores de antes):

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/

En este caso, la función podría modificarse para targetArraypasarla como argumento en lugar de codificarse en el cierre.


ACTUALIZACIÓN2:

Si bien mi solución anterior puede funcionar y ser (con suerte más) legible, creo que la "mejor" forma de manejar el concepto que describí es hacer algo un poco diferente. El "problema" con la solución anterior es que el indexOfinterior del bucle hace que la matriz de destino se repita completamente para cada elemento de la otra matriz. Esto se puede "arreglar" fácilmente mediante el uso de una "búsqueda" (un mapa ... un literal de objeto JavaScript). Esto permite dos bucles simples, sobre cada matriz. Aquí hay un ejemplo:

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/

La desventaja de esta solución es que solo se pueden usar (correctamente) números y cadenas (y booleanos), porque los valores se convierten (implícitamente) en cadenas y se establecen como las claves del mapa de búsqueda. Esto no es exactamente bueno / posible / fácil de hacer para valores no literales.

Ian
fuente
2
Excelente ejemplo, el segundo ejemplo es simplemente brillante.
Sorin Haidau
¿Por qué estás usando bucles mientras podrías usar algunos o findIndex?
Soy yo ... Alex
1
"algunos" simplifica enormemente el código. Además, anyMatchInArray ([1,2,3, "gatos", "4"], ["1", 4]) sería verdadero. Por último, esto puede ser más eficaz SI tuvo una gran cantidad de búsquedas y almacenó en caché el TargetMap. Aun así, probablemente podría haber aumentos de rendimiento. Por ejemplo, supongo que "found = toMatch [i]! == undefined" sería más eficiente y, en algunos casos, mejor (para que no evalúe "" o 0 a falso)
csga5000
"De lo contrario, volverá undefined... para eso !!está", eso está mal. Devolverá la oposición booleana de !.
AlienWebguy
41

Solución ES6:

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

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

A diferencia de esto: debe contener todos los valores.

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

Espero, será de ayuda.

tanvir993
fuente
¿Hay alguna manera de obtener el índice de los valores de array2 de array1 ?
Anzil KhaN
1
En ese caso, podemos usar "filtro" en lugar de "algunos". Luego devolverá una matriz en lugar de booleana y puede acceder fácilmente al valor desde allí.
tanvir993
29

Podrías usar lodash y hacer:

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

Establecer intersección se realiza en ambas colecciones produciendo una matriz de elementos idénticos.

Justin Cuaresma
fuente
No es óptimo en términos de rendimiento, ya que para este problema es suficiente encontrar la primera coincidencia, mientras intersectionque seguirá comparando incluso después de encontrar la primera coincidencia para encontrarlos a todos. Es como usar filtercuando solo necesitas find.
Alexander
29

Usando 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"]));

Vadim Gremyachev
fuente
Esto sufre el mismo problema que las funciones de la biblioteca, como _.intersection, ya que seguirá buscando coincidencias incluso después de encontrar una. Sin embargo, para matrices pequeñas obviamente no importa.
JHH
13
const areCommonElements = (arr1, arr2) => {
    const arr2Set = new Set(arr2);
    return arr1.some(el => arr2Set.has(el));
};

O incluso puede tener un mejor rendimiento si primero descubre cuál de estas dos matrices es más larga y Setbusca la matriz más larga, mientras aplica el somemétodo en la más corta:

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));
};
Alejandro
fuente
3
Si bien las personas siguen publicando soluciones con anidamiento indexOfy includes, usted es el primero en responder con la solución basada en conjuntos más eficiente, utilizando el nativo Set, 4 años después de que se introdujo en EcmaScript. +1
trincot
9

Encontré esta sintaxis corta y dulce para que coincida con todos o algunos elementos entre dos matrices. Por ejemplo

// operación OR. encuentre si alguno de los elementos de array2 existe en array1. Esto volverá tan pronto como haya una primera coincidencia ya que algunos métodos se rompen cuando la función devuelve VERDADERO

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

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

// imprime VERDADERO

// Y operación. encuentre si todos los elementos de array2 existen en array1. Esto volverá tan pronto como no haya una primera coincidencia ya que algunos métodos se rompen cuando la función devuelve VERDADERO

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

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

// imprime FALSO

Espero que ayude a alguien en el futuro!

kashpatel
fuente
Realmente me gustó la segunda parte de la pregunta, para que funcione para ES5, me gustó:! Array2.some (function (ele) {return array1.indexOf (ele) === -1});
Friesgaard
6

Puede usar una llamada anidada Array.prototype.some. Esto tiene el beneficio de que se rescatará en la primera coincidencia en lugar de otras soluciones que se ejecutarán a través del bucle anidado completo.

p.ej.

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

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

Aquí hay un caso interesante que pensé que debería compartir.

Digamos que tiene una matriz de objetos y una matriz de filtros seleccionados.

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

const filters = ['foo'];

Para aplicar los filtros seleccionados a esta estructura podemos

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

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

Escribí 3 soluciones. Esencialmente hacen lo mismo. Vuelven verdad tan pronto como llegan true. Escribí las 3 soluciones solo para mostrar 3 formas diferentes de hacer las cosas. Ahora, depende de lo que más te guste. Puedes usar performance.now () para verificar el rendimiento de una solución u otra. En mis soluciones también estoy verificando qué matriz es la más grande y cuál es la más pequeña para que las operaciones sean más eficientes.

La tercera solución puede no ser la más linda pero es eficiente. Decidí agregarlo porque en algunas entrevistas de codificación no está permitido usar métodos integrados.

Por último, claro ... podemos llegar a una solución con 2 NESTED para bucles (la forma de fuerza bruta) pero desea evitar eso porque la complejidad del tiempo es mala O (n ^ 2) .

Nota:

en lugar de usar .includes()como lo hacían otras personas, puedes usarlo .indexOf(). si lo hace, simplemente verifique si el valor es mayor que 0. Si el valor no existe, obtendrá -1. si existe, te dará más de 0.

indexOf () vs incluye ()

¿Cuál tiene mejor rendimiento ? indexOf()por un momento, pero incluye es más legible en mi opinión.

Si no me equivoco .includes()y indexOf()uso bucles detrás de la escena, entonces estarás en O (n ^ 2) cuando los uses .some().

USANDO bucle

 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 DE MAPAS Complejidad de tiempo 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;
};

Código en mi: stackblitz

No soy un experto en rendimiento ni BigO, así que si algo que dije está mal, hágamelo saber.

devpato
fuente
3

¿Qué pasa con el uso de una combinación de algunos / findIndex e indexOf?

Entonces algo como esto:

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

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

Para hacerlo más legible, puede agregar esta funcionalidad al objeto Array en sí.

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: Si desea hacer algo con un predicado, puede reemplazar el indexOf interno con otro findIndex y un predicado

Soy yo ... Alex
fuente
3

Mi solución aplica Array.prototype.some () y Array.prototype.includes () ayudantes de matriz que también hacen su trabajo bastante eficiente

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;

Svyatoslav Gerasimov
fuente
¿Hay alguna forma de obtener el índice de elementos incluidos de originalFruits?
Anzil KhaN
3

Solo una solución más

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

Compruebe si a1 contiene todos los elementos de a2

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

Se puede hacer simplemente iterando a través de la matriz principal y verifica si otra matriz contiene o no el elemento objetivo.

Prueba esto:

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

Ritesh Kumar Gupta
fuente
2

Con guiones bajos

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
fguillen
fuente
2
Personalmente, aunque me gustan los subrayados, este es un ejemplo clásico de cómo puede verse un código complicado. No solo es difícil de comprender como código de subrayado, sino que desde un punto de vista de codificación general, lo mismo también es cierto (por ejemplo, la palabra "cada" no me viene a la mente cuando quiero encontrar el índice de algo en una matriz, pero "indexOf" hace). Deberíamos evitar el uso innecesario de herramientas de terceros cuando, por unos pocos caracteres adicionales, se podría proporcionar una solución de JavaScript pura. El uso de underscorejs por el simple hecho de que su solución se asocia estrechamente con el código de terceros.
csharpforevermore
@csharpforevermore Supongo que esto es una cuestión de gustos, usted dice que esta solución es más complicada que todas las otras que usan indexOf, creo que es lo contrario :). Por otro lado, estoy de acuerdo en tratar de no agregar bibliotecas externas si realmente no son necesarias, pero no estoy realmente obsesionado con eso, las bibliotecas de terceros no solo ofrecen funcionalidades útiles sino también funcionalidades sólidas . Por ejemplo: ¿ha probado todos los casos límite y navegadores principales con su solución? ... (por cierto, everyno está tratando de encontrar un índice en una lista sino evaluando algo en cada elemento de la lista)
fguillen
2

Agregar al prototipo de matriz

Descargo de responsabilidad: muchos aconsejarían en contra de esto La única vez que realmente sería un problema era si una biblioteca agregaba una función prototipo con el mismo nombre (que se comportaba de manera diferente) o algo así.

Código:

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

Sin usar las funciones de flecha 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
csga5000
fuente
2

Vanilla JS con coincidencia parcial e insensible

El problema con algunos enfoques anteriores es que requieren una coincidencia exacta de cada palabra . Pero, ¿qué sucede si desea proporcionar resultados para coincidencias parciales?

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

Esto es útil cuando desea proporcionar un cuadro de búsqueda donde los usuarios escriben palabras y los resultados pueden tener esas palabras en cualquier orden, posición y caso.

SntsDev
fuente
2

Actualice la respuesta de @Paul Grimshaw, use includesinsteed of indexOfpara leer más

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

Aninh Anh Huy
fuente
1

Se me ocurrió una solución en el nodo usando subrayado js como este:

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

Personalmente, usaría la siguiente función:

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

El método "toString ()" siempre usará comas para separar los valores. Solo funcionará realmente con tipos primitivos.

csharpforevermore
fuente
2
Esto no funcionará cuando los elementos estén al principio o al final de la matriz, o en un orden diferente.
DanielM
1
-1 porque como dijo DanielM esto está roto. Usted podría anteponer y añadir una coma para arrayAsString como una solución, pero la verdad es que apenas se parece como una solución excesivamente complicado para cadenas de uso.
JHH
0

La matriz .filter () con una llamada anidada a .find () devolverá todos los elementos de la primera matriz que son miembros de la segunda matriz. Verifique la longitud de la matriz devuelta para determinar si alguna de la segunda matriz estaba en la primera matriz.

getCommonItems(firstArray, secondArray) {
  return firstArray.filter((firstArrayItem) => {
    return secondArray.find((secondArrayItem) => {
      return firstArrayItem === secondArrayItem;
    });
  });
}
Neoheurista
fuente
¿Hay alguna manera de "limpiar" la matriz? ¿Como eliminar los valores en la segunda matriz si existen en la primera?
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);
Naveen Koti
fuente
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;
Vasudev
fuente