Verifique se uma matriz contém algum elemento de outra matriz em JavaScript

406

Eu tenho uma matriz de destino ["apple","banana","orange"]e quero verificar se outras matrizes contêm algum dos elementos da matriz de destino.

Por exemplo:

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

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

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

Como posso fazer isso em JavaScript?

Alex
fonte
3
Use um forloop e itere sobre a matriz de destino. Se cada elemento está contido dentro da matriz atual (uso current.indexOf(elem) !== -1), em seguida, eles estão todos lá.
Blender
21
@ Alex escolher uma resposta cara, é rude deixar uma resposta desmarcada, especialmente com essas muitas boas respostas. Eu escolheria o sublinhado / lodash se fosse você.
precisa saber é o seguinte
11
@LeonGaban Eu discordo. Eu não importaria uma biblioteca apenas para executar esta operação.
devpato
2
@devpato sim, mudei de idéia, a solução ES6 é minha favorita
Leon Gaban
E arr1.some (el1 => arr2.inclui (el1)); ?
Walaszka 11/06

Respostas:

551

JS de baunilha

ES2016:

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

ES6:

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

Como funciona

some(..)verifica cada elemento da matriz em relação a uma função de teste e retorna true se algum elemento da matriz passa na função de teste, caso contrário, ele retorna false. indexOf(..) >= 0e includes(..)ambos retornam true se o argumento fornecido estiver presente na matriz.

Paul Grimshaw
fonte
48
Este código precisa de uma explicação - este trabalho poder, mas não há nenhum valor, porque ninguém está aprendendo nada
TolMera
6
Array.prototype.some () verifica cada elemento da matriz em relação a uma função de teste e retorna truese algum elemento da matriz passa na função de teste. Caso contrário, ele retornará false.
Hendeca 15/08/17
2
essa deve ser a resposta correta! Excelente usando o ES6
Nacho
11
É esperado que meu resultado seja em [false, false, false]vez de uma matriz vazia []?
Batman
@Batman: O resultado é verdadeiro / falso, mas você pode adaptar a solução do Sr. skyisred
0zkr PM
230

js de baunilha

/**
 * @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
fonte
10
Ótima solução! some()é rad. Sai assim que algo coincide.
averydev
6
evento mais arrumado assim:arr.some(v=> haystack.indexOf(v) >= 0)
Paul Grimshaw
85
Também disponível no ES2016arr.some(v => haystack.includes(v))
loganfsmyth
5
em uma linha arr1.some(v => arr2.indexOf(v) >= 0).
Webjay
11
Por enquanto, talvez seja melhor evitar o uso includes, como aparentemente não é suportado no IE: stackoverflow.com/questions/36574351/...
Shafique Jamal
72

Se você não se opõe a usar uma biblioteca, http://underscorejs.org/ possui um método de interseção, que pode simplificar isso:

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]

A função de interseção retornará uma nova matriz com os itens correspondentes e, se não corresponder, retornará uma matriz vazia.

willz
fonte
3
Eu usei isso várias vezes, mas observe que a pergunta era sobre verificar se existe algum elemento na outra matriz, para não produzir a interseção inteira. Em termos de desempenho, há uma enorme diferença se as matrizes forem grandes, pois no primeiro caso, você pode resgatar assim que encontrar uma correspondência.
JHH 22/01
11
O lodash é muito mais legível que o Javascript vanilla, as bibliotecas como a do Ramda sempre devem ser usadas em vez do vanilla imho. Melhor para todos os devs ...
Leon Gaban
52

ES6 (mais 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));

Sublinhado

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

lusk
fonte
11
esta é a solução mais simples e declarativa #
strider
Eu sei que sou muito tarde para isso, mas para verificar o console se jsFiddle adicionar JQuery Edge e ligar Firebug Lite
Rojo
Link do JSperf quebrado
DarckBlezzer
Existe alguma diferença na complexidade de tempo e espaço? Qual seria a melhor solução em relação à complexidade?
nacho
41

Se você não precisar de coerção de tipo (por causa do uso de indexOf), tente algo como o seguinte:

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

Onde arrcontém os itens de destino. No final, foundmostrará se a segunda matriz teve pelo menos uma partida contra o alvo.

Claro, você pode trocar números por qualquer coisa que queira usar - as strings são boas, como no seu exemplo.

E no meu exemplo específico, o resultado deve ser trueporque a segunda matriz 3existe no destino.


ATUALIZAR:

Aqui está como eu organizaria isso em uma função (com algumas pequenas alterações 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/

Nesse caso, a função pode ser modificada para targetArrayser passada como argumento em vez de codificada no fechamento.


UPDATE2:

Embora minha solução acima possa funcionar e ser (espero que mais) legível, acredito que a maneira "melhor" de lidar com o conceito que descrevi é fazer algo um pouco diferente. O "problema" com a solução acima é que o indexOfinterior do loop faz com que o array de destino seja repetido completamente para cada item do outro array. Isso pode ser facilmente "consertado" usando uma "pesquisa" (um mapa ... um literal de objeto JavaScript). Isso permite dois loops simples, sobre cada matriz. Aqui está um exemplo:

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/

A desvantagem desta solução é que apenas números e cadeias (e booleanos) podem ser usados ​​(corretamente), porque os valores são (implicitamente) convertidos em cadeias e configurados como as chaves do mapa de pesquisa. Isso não é exatamente bom / possível / fácil para valores não literais.

Ian
fonte
2
Exemplo excelente, o segundo exemplo é simplesmente brilhante.
Sorin Haidau
Por que você está usando loops enquanto pode usar alguns ou findIndex?
Sou eu ... Alex
11
"some" simplifica bastante o código. Além disso, anyMatchInArray ([1,2,3, "cats", "4"], ["1", 4]) seria verdadeiro. Por fim, isso pode ser mais eficiente se você tiver um grande número de pesquisas e armazenar em cache o targetMap. Mesmo assim, provavelmente poderia haver aumentos de desempenho. Por exemplo, eu acho que "found = toMatch [i]! == undefined" teria melhor desempenho e, em alguns casos, melhor (para que você não avalie "" ou 0 como falso)
csga5000
"caso contrário, ele retornará undefined... !!é para isso que serve" - ​​isso está errado. Retornará a oposição booleana de !.
AlienWebguy
41

Solução ES6:

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

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

Ao contrário: Must contém todos os valores.

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

Espero que seja útil.

tanvir993
fonte
Existe alguma maneira de obter o índice de valores arra2 de array1 ??
Anzil Khan
11
Nesse caso, podemos usar "filtro" em vez de "alguns". Em seguida, ele retornará uma matriz em vez de booleano e você poderá acessar facilmente o valor a partir daí.
tanvir993
29

Você pode usar o lodash e fazer:

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

A interseção do conjunto é feita nas duas coleções, produzindo uma matriz de elementos idênticos.

Justin Cuaresma
fonte
Não é o ideal em termos de desempenho, pois para esse problema é suficiente encontrar a primeira correspondência, enquanto intersectioncontinua comparando mesmo depois de encontrar a primeira correspondência para encontrar todas elas. É como usar filterquando você precisa 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
fonte
Isso sofre do mesmo problema que as funções da biblioteca, como _.intersection, na medida em que continuará procurando correspondências mesmo depois de encontrar uma. Para pequenas matrizes, obviamente, não importa.
JHH 22/01
13
const areCommonElements = (arr1, arr2) => {
    const arr2Set = new Set(arr2);
    return arr1.some(el => arr2Set.has(el));
};

Ou você pode até ter um desempenho melhor se descobrir primeiro qual dessas duas matrizes é mais longa e compensar Seta matriz mais longa, enquanto aplica o somemétodo na menor:

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));
};
Alexander
fonte
3
Enquanto as pessoas continuam postando soluções com aninhamento indexOfe includes, você é o primeiro a responder com a solução baseada em conjunto mais eficiente, usando a nativa Set, quatro anos depois de ter sido introduzida no EcmaScript. +1
trincot 16/02
9

Eu achei essa sintaxe curta e agradável para combinar todos ou alguns elementos entre duas matrizes. Por exemplo

// operação OR. encontre se algum dos elementos da matriz2 existe na matriz1. Isso retornará assim que houver uma primeira correspondência, pois algum método será interrompido quando a função retornar TRUE

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

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

// imprime TRUE

// operação AND. encontre se todos os elementos do array2 existem no array1. Isso retornará assim que não houver uma primeira correspondência, pois algum método será interrompido quando a função retornar TRUE

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

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

// imprime FALSE

Espero que ajude alguém no futuro!

kashpatel
fonte
Gostei muito da segunda parte da pergunta, para fazê-lo funcionar no ES5, como:! Array2.some (function (ele) {return array1.indexOf (ele) === -1});
Friesgaard
6

Você pode usar uma chamada Array.prototype.some aninhada. Isso tem o benefício de ser resgatado na primeira partida, em vez de outras soluções que serão executadas no loop aninhado completo.

por exemplo.

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

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

Aqui está um caso interessante que pensei que deveria compartilhar.

Digamos que você tenha uma matriz de objetos e uma matriz de filtros selecionados.

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

const filters = ['foo'];

Para aplicar os filtros selecionados a essa estrutura, 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'] }
// ]
user5470921
fonte
4

Eu escrevi 3 soluções. Essencialmente, eles fazem o mesmo. Eles retornam verdadeiros assim que são alcançados true. Eu escrevi as 3 soluções apenas para mostrar três maneiras diferentes de fazer as coisas. Agora, depende do que você mais gosta. Você pode usar o performance.now () para verificar o desempenho de uma solução ou de outra. Nas minhas soluções, também estou verificando qual matriz é a maior e qual é a menor para tornar as operações mais eficientes.

A terceira solução pode não ser a mais fofa, mas é eficiente. Decidi adicioná-lo porque em algumas entrevistas de codificação você não tem permissão para usar métodos internos.

Por fim, claro ... podemos encontrar uma solução com 2 NESTED para loops (a maneira da força bruta), mas você deseja evitar isso porque a complexidade do tempo é ruim O (n ^ 2) .

Nota:

em vez de usar .includes()como outras pessoas, você pode usar .indexOf(). se você apenas verificar se o valor é maior que 0. Se o valor não existir, você receberá -1. se existir, fornecerá mais que 0.

indexOf () vs includes ()

Qual deles tem melhor desempenho ? indexOf()por um pouco, mas inclui é mais legível na minha opinião.

Se não me engano .includes()e indexOf()usar loops nos bastidores, você estará em O (n ^ 2) ao usá-los .some().

Loop USING

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

UTILIZAÇÃO DE MAPAS Complexidade temporal 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 no meu: stackblitz

Eu não sou especialista em desempenho nem em BigO; se algo que eu disse estiver errado, me avise.

devpato
fonte
3

Que tal usar uma combinação de some / findIndex e indexOf?

Então, algo como isto:

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

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

Para torná-lo mais legível, você pode adicionar essa funcionalidade ao próprio objeto 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;
}

Nota: Se você quiser fazer algo com um predicado, poderá substituir o indexOf interno por outro findIndex e um predicado

Sou eu ... Alex
fonte
3

Minha solução aplica Array.prototype.some () e Array.prototype.includes () ajudantes de matriz que também fazem seu trabalho de maneira 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
fonte
Existe alguma maneira de obter o índice de inclui itens de originalFruits ??
Anzil Khan
3

Apenas mais uma solução

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

Verifique se a1 contém todo o elemento de a2

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

Isso pode ser feito através da iteração na matriz principal e verifique se outra matriz contém algum elemento de destino ou não.

Tente o seguinte:

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

Ritesh Kumar Gupta
fonte
2

With 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
fguillen
fonte
2
Pessoalmente, embora eu goste de underscorejs, este é um exemplo clássico de como o código complicado pode parecer. Não é apenas difícil de entender como código underscorejs, mas do ponto de vista geral da codificação, o mesmo também é verdadeiro (por exemplo, a palavra "every" não vem à mente quando eu quero encontrar o índice de algo em uma matriz, mas "indexOf" faz). Devemos evitar o uso desnecessário de ferramentas de terceiros quando, para alguns caracteres extras, uma solução JavaScript pura puder ser fornecida. O uso de underscorejs para isso significa que sua solução fica fortemente acoplada ao código de terceiros.
Csharpforevermore 28/05
@csharpforevermore Suponho que isso é uma questão de gosto, você diz que esta solução é mais complicada do que todas as outras usando indexOf, acho que é o contrário :). Por outro lado, concordo em tentar não adicionar bibliotecas externas se elas não forem realmente necessárias, mas não sou muito obsessivo com isso, as bibliotecas de terceiros não apenas oferecem funcionalidades úteis, mas também funcionalidades sólidas . Por exemplo: você já testou todos os casos extremos e os principais navegadores com sua solução? .. (a propósito, everynão está tentando encontrar um índice em uma lista, mas avaliando algo em todos os elementos da lista)
fguillen
2

Adicionando ao protótipo de matriz

Aviso Legal: Muitos aconselham fortemente contra isso. A única vez que realmente seria um problema era se uma biblioteca adicionasse uma função de protótipo com o mesmo nome (que se comportasse de maneira diferente) ou algo assim.

Código:

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

Sem usar funções de seta 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
fonte
2

JS de baunilha com correspondência parcial e sem distinção entre maiúsculas e minúsculas

O problema com algumas abordagens anteriores é que elas exigem uma correspondência exata de cada palavra . Mas, e se você quiser fornecer resultados para correspondências parciais?

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

Isso é útil quando você deseja fornecer uma caixa de pesquisa na qual os usuários digitam palavras e os resultados podem ter essas palavras em qualquer ordem, posição e caso.

SntsDev
fonte
2

Atualize a resposta do @Paul Grimshaw, use o includesinsteed de indexOfpara mais legibilidade

Vamos encontrar = arr1.some (r => arr2.indexOf (r)> = 0)
vamos encontrar = arr1.some (r => arr2.includes (r))

Anh Huy
fonte
1

Eu vim com uma solução no nó usando sublinhado js como este:

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

Pessoalmente, eu usaria a seguinte função:

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

O método "toString ()" sempre usará vírgulas para separar os valores. Realmente só funcionará com tipos primitivos.

csharpforevermore
fonte
2
Isso não funcionará quando os elementos estiverem no início ou no final da matriz ou em uma ordem diferente.
DanielM
11
-1 porque, como DanielM disse, isso está quebrado. Você pode acrescentar e acrescentar uma vírgula ao arrayAsString como uma solução alternativa, mas, honestamente, parece uma solução muito complicada para usar seqüências de caracteres.
JHH 22/01
0

Matriz .filter () com uma chamada aninhada para .find () retornará todos os elementos na primeira matriz que são membros da segunda matriz. Verifique o comprimento da matriz retornada para determinar se alguma da segunda matriz estava na primeira matriz.

getCommonItems(firstArray, secondArray) {
  return firstArray.filter((firstArrayItem) => {
    return secondArray.find((secondArrayItem) => {
      return firstArrayItem === secondArrayItem;
    });
  });
}
Neoheurista
fonte
Existe uma maneira de "limpar" a matriz? Como excluir os valores na segunda matriz, se existirem na primeira?
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
fonte
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
fonte