Existe uma maneira de retornar a diferença entre duas matrizes em JavaScript?
Por exemplo:
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
// need ["c", "d"]
javascript
arrays
array-difference
John Adawan
fonte
fonte
O(a1.length x log(a2.length))
- esse desempenho é possível em JavaScript?Respostas:
Presumo que você esteja comparando uma matriz normal. Caso contrário, você precisará alterar o loop for para um loop for .. in .
Uma solução melhor, se você não se importa com a compatibilidade com versões anteriores, está usando o filtro. Mas ainda assim, esta solução funciona.
fonte
var a1 = ['a', 'b'];
evar a2 = ['a', 'b', 'c', 'd', 'b'];
, ele irá retornar resposta errada , ou seja,['c', 'd', 'b']
em vez de['c', 'd']
.function diff2(a, b) { var i, la = a.length, lb = b.length, res = []; if (!la) return b; else if (!lb) return a; for (i = 0; i < la; i++) { if (b.indexOf(a[i]) === -1) res.push(a[i]); } for (i = 0; i < lb; i++) { if (a.indexOf(b[i]) === -1) res.push(b[i]); } return res; }
Existe uma maneira melhor de usar o ES7:
Interseção
Pois
[1,2,3] [2,3]
isso cederá[2,3]
. Por outro lado, para[1,2,3] [2,3,5]
retornará a mesma coisa.Diferença
Pois
[1,2,3] [2,3]
isso cederá[1]
. Por outro lado, para[1,2,3] [2,3,5]
retornará a mesma coisa.Para uma diferença simétrica , você pode fazer:
Dessa forma, você obterá uma matriz contendo todos os elementos de arr1 que não estão em arr2 e vice-versa
Como @Joshaven Potter apontou em sua resposta, você pode adicioná-lo ao Array.prototype para que ele possa ser usado assim:
fonte
< 0
vez de== -1
Array
diferença é o chamadoset operation
, porque a pesquisa de propriedades é o trabalho próprio deSet
s, que são ordens de magnitude mais rápidas queindexOf
/includes
. Simplificando, sua solução é muito ineficiente e bastante lenta.Set
valores devem ser únicos, não?[1,2,3] [2,3,5]
dado que os números são únicos, mas se você tivesse dito[1,1,2,3] [1,2,3,5]
e esperado[1]
que não poderia usarSet
. Sua solução também não funcionaria: - / Acabei criando esta função porque não conseguia descobrir uma maneira satisfatória de fazê-la de forma mais sucinta. Se você tem alguma idéia de como fazer isso, eu adoraria saber!Array.includes()
recurso ES7 não é em vez do ES6? (1) (2) - e para continuar, com o ES6 você pode usar ,Array.some()
por exemplolet intersection = aArray.filter(a => bArray.some(b => a === b))
, não?Mostrar snippet de código
Nota indexOf e filter não estão disponíveis em ie antes de ie9.
fonte
[1,2,3].diff([3,4,5])
ela retornará em[1,2]
vez de[1,2,4,5]
não resolver o problema na pergunta original, algo para estar ciente.Essa é de longe a maneira mais fácil de obter exatamente o resultado que você está procurando, usando o jQuery:
diff
agora contém o que estava emold_array
que não está emnew_array
fonte
{a: 1} != {a: 1}
) ( prova ).not
uma matriz, o jQuery usa seu utilitário interno,.grep()
que é especificamente para filtrar matrizes. Não vejo isso mudando.O método da diferença no Underscore (ou seu substituto drop-in, Lo-Dash ) também pode fazer isso:
Como em qualquer função Underscore, você também pode usá-la em um estilo mais orientado a objetos:
fonte
JavaScript simples
Existem duas possíveis interpretações para "diferença". Vou deixar você escolher qual você quer. Digamos que você tenha:
Se você deseja obter
['a']
, use esta função:Se você deseja obter
['a', 'c']
(todos os elementos contidos em uma1
oua2
, mas não ambos - a chamada diferença simétrica ), use esta função:Lodash / Sublinhado
Se você estiver usando lodash, poderá usar
_.difference(a1, a2)
(caso 1 acima) ou_.xor(a1, a2)
(caso 2).Se você estiver usando o Underscore.js, poderá usar o
_.difference(a1, a2)
função para o caso 1.Conjunto ES6, para matrizes muito grandes
O código acima funciona em todos os navegadores. No entanto, para grandes matrizes com mais de 10.000 itens, torna-se bastante lento, porque possui complexidade de O (n²). Em muitos navegadores modernos, podemos tirar proveito do
Set
objeto ES6 para acelerar as coisas. O Lodash usa automaticamenteSet
quando está disponível. Se você não estiver usando o lodash, use a seguinte implementação, inspirada na postagem do blog de Axel Rauschmayer :Notas
O comportamento de todos os exemplos pode ser surpreendente ou não óbvio se você se preocupa com -0, +0, NaN ou matrizes esparsas. (Para a maioria dos usos, isso não importa.)
fonte
Para obter a diferença simétrica, você precisa comparar as matrizes de ambas as formas (ou de todas as formas no caso de várias matrizes)
ES7 (ECMAScript 2016)
ES6 (ECMAScript 2015)
ES5 (ECMAScript 5.1)
Exemplo:
Diferença entre matrizes de objetos
Exemplo:
fonte
Uma abordagem mais limpa no ES6 é a seguinte solução.
Diferença
Interseção
União disjuntiva (diferença simétrica)
fonte
a1 = ['a', 'b', 'e']
: e não será extraído.Você pode usar um conjunto neste caso. É otimizado para este tipo de operação (união, interseção, diferença).
Verifique se ele se aplica ao seu caso, pois ele não permite duplicatas.
fonte
Set
função sem ter que se tudo o resto ...Mesclar as duas matrizes, os valores exclusivos aparecerão apenas uma vez, para que indexOf () seja o mesmo que lastIndexOf ().
fonte
para subtrair uma matriz de outra, basta usar o snippet abaixo:
Ele retornará ['1,' 2 ',' 6 '] que são itens da primeira matriz que não existem na segunda.
Portanto, de acordo com o exemplo de problema, o código a seguir é a solução exata:
fonte
Com a chegada do ES6 com sets e operador splat (no momento em que funciona apenas no Firefox, verifique a tabela de compatibilidade ), é possível escrever o seguinte liner:
o que resultará em
[ "c", "d" ]
.fonte
b.filter(x => !a.indexOf(x)))
O(n + m)
sua solução éO(n * m)
onde n e m são comprimentos de matrizes. Pegue listas longas e minha solução será executada em segundos, enquanto a sua levará horas.a.filter(x => !b1.has(x))
é mais simples. E observe que a especificação exige apenas que a complexidade sejan * f(m) + m
comf(m)
sublinear em média. É melhor quen * m
, mas não necessariamenten + m
.var difference = [...new Set([...a].filter(x => !b1.has(x)))];
Por que você está criando uma matriz 'a' duplicada? Por que você está transformando o resultado do filtro em um conjunto e depois voltando à matriz? Isso não é equivalente avar difference = a.filter(x => !b1.has(x));
Abordagem funcional com o ES2015
Computar as
difference
duas matrizes é uma dasSet
operações. O termo já indica que oSet
tipo nativo deve ser usado, para aumentar a velocidade da pesquisa. De qualquer forma, existem três permutações ao calcular a diferença entre dois conjuntos:Aqui está uma solução funcional que reflete essas permutações.
Esquerda
difference
:Direita
difference
:differencer
é trivial. É apenasdifferencel
com argumentos invertidos. Você pode escrever uma função por conveniência:const differencer = flip(differencel)
. Isso é tudo!Simétrico
difference
:Agora que temos a esquerda e a direita, a implementação da simétrica também
difference
é trivial:Acho que este exemplo é um bom ponto de partida para obter uma impressão do que significa programação funcional:
Programação com blocos de construção que podem ser conectados de várias maneiras diferentes.
fonte
Uma solução usando
indexOf()
será boa para pequenas matrizes, mas à medida que elas crescem, o desempenho do algoritmo se aproximaO(n^2)
. Aqui está uma solução que terá melhor desempenho para matrizes muito grandes usando objetos como matrizes associativas para armazenar as entradas da matriz como chaves; ele também elimina entradas duplicadas automaticamente, mas funciona apenas com valores de string (ou valores que podem ser armazenados com segurança como strings):fonte
A resposta acima de Joshaven Potter é ótima. Mas ele retorna elementos na matriz B que não estão na matriz C, mas não o contrário. Por exemplo, se
var a=[1,2,3,4,5,6].diff( [3,4,5,7]);
for exibido: ==>[1,2,6]
, mas não[1,2,6,7]
, qual é a diferença real entre os dois. Você ainda pode usar o código de Potter acima, mas simplesmente refazer a comparação uma vez para trás também:Isso deve gerar:
[ 1, 2, 6, 7 ]
fonte
Outra maneira de resolver o problema
Além disso, você pode usar a sintaxe da função de seta:
fonte
fonte
difference
como função em uma versão futura e essa função tiver uma assinatura de função diferente da sua, ele quebrará seu código ou bibliotecas estrangeiras que usam essa função.Solução muito simples com a função de filtro do JavaScript:
fonte
Que tal agora:
Dessa forma, você pode fazer isso
array1.diff(array2)
para obter a diferença (no entanto, a complexidade do tempo para o algoritmo é horrível - O (array1.length x array2.length) eu acredito)fonte
Usando http://phrogz.net/JS/ArraySetMath.js, você pode:
fonte
isso é funciona para mim
fonte
filter
)fn
Parâmetro de retorno de chamada opcional que permite especificar como comparar itens da matrizfonte
length
valores em cache . Já é propriedade comum. jsperf.com/array-length-cachingIsso está funcionando: basicamente mescle as duas matrizes, procure as duplicatas e envie o que não é duplicado para uma nova matriz, que é a diferença.
fonte
// abordagem es6
fonte
Complexidade simétrica e linear . Requer ES6.
fonte
mais uma resposta, mas parece que ninguém mencionou o jsperf, onde eles comparam vários algoritmos e suporte de tecnologia: https://jsperf.com/array-difference-javascript parece que usar filtro obtém os melhores resultados. obrigado
fonte
Apenas pensando ... por uma questão de desafio ;-) funcionaria ... (para matrizes básicas de strings, números, etc.) sem matrizes aninhadas
Observe que a classificação provavelmente não será como observada acima ... mas, se desejar, chame .sort () na matriz para classificá-la.
fonte
Eu queria uma função semelhante que incluísse uma matriz antiga e uma nova matriz e me desse uma matriz de itens adicionados e uma matriz de itens removidos, e queria que fosse eficiente (portanto, não contém.).
Você pode brincar com minha solução proposta aqui: http://jsbin.com/osewu3/12 .
Alguém pode ver algum problema / melhoria nesse algoritmo? Obrigado!
Listagem de código:
fonte
Eu estava procurando uma resposta simples que não envolvesse o uso de bibliotecas diferentes e criei uma que não acho que tenha sido mencionada aqui. Não sei o quão eficiente é nem nada, mas funciona;
Para o meu código, também preciso duplicar, mas acho que isso nem sempre é preferido.
Eu acho que a principal desvantagem é que é potencialmente comparar muitas opções que já foram rejeitadas.
fonte
littlebit correção para a melhor resposta
isso levará em consideração o tipo atual de elemento. b / c quando fazemos um [a1 [i]], ele converte um valor em string do seu valor original, então perdemos o valor real.
fonte