Usar lodash para comparar matrices (existencia de elementos sin orden)

125

Sé que puedo hacerlo usando bucles, pero estoy tratando de encontrar una forma elegante de hacerlo:

Tengo dos matrices:

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];

Quiero usar lodashpara confirmar que las dos matrices anteriores son iguales. Por "lo mismo" quiero decir que no hay ningún elemento array1que no esté contenido en array2.

En términos de verificar la igualdad entre estos elementos:

['a', 'b'] == ['b', 'a'] 

o

['a', 'b'] == ['a', 'b'] 

Ambos funcionan ya que las letras siempre estarán en orden.

pQuestions123
fuente

Respuestas:

224

Si ordena la matriz externa, puede usarla, _.isEqual()ya que la matriz interna ya está ordenada.

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];
_.isEqual(array1.sort(), array2.sort()); //true

Tenga en cuenta que .sort()mutará las matrices. Si eso es un problema para usted, primero haga una copia usando (por ejemplo) .slice()o el operador de extensión ( ...).

O haga lo que recomienda Daniel Budick en un comentario a continuación:

_.isEqual(_.sortBy(array1), _.sortBy(array2))

Lodash's sortBy()no mutará la matriz.

Trott
fuente
6
Tenga en cuenta que array.sort () está mutando la matriz original. Quizás este podría ser mejor: var array1 = [['a', 'b'], ['b', 'c']]; var matriz2 = [['b', 'c'], ['a', 'b']]; _.isEqual ([... matriz1]. ordenación (), [... matriz2]. ordenación ()); // cierto
Yaniv Efraim
3
Se agregaron dos oraciones señalando que .sort()muta y sugiriendo opciones para copiar primero si eso es un problema para el usuario.
Trott
6
Si ya está usando lodash, puede hacerlo _.isEqual(_.sortBy(array1), _sortBy(array2))para evitar la mutación.
Daniel Budick
1
@DanielBudick ¡Gracias! He agregado eso a la respuesta. Gran sugerencia.
Trott
33

Puedes usar lodashs xorpara esto

doArraysContainSameElements = _.xor(arr1, arr2).length === 0

Si considera que la matriz [1, 1] es diferente a la matriz [1], entonces puede mejorar el rendimiento un poco así:

doArraysContainSameElements = arr1.length === arr2.length === 0 && _.xor(arr1, arr2).length === 0
Stephan Hoyer
fuente
1
Las matrices deben ordenarse de todos modos.
Sovattha Sok
1
Esta debería ser la mejor manera para una versión más nueva
Leonardo
@Sovattha Sok No es necesario ordenar las matrices. Hacerlo _.xor([3,4], [4,3]).length == 0te hará verdadero.
Nikolay
1
Una advertencia: esta técnica funciona bien para matrices "pequeñas", pero puede ser un problema para el rendimiento y la memoria si las matrices son enormes y en su mayoría diferentes porque _.xor () seguirá superando la primera diferencia. En otras palabras, no regresa rápidamente a la primera diferencia detectada.
XDS
6

Por "lo mismo" me refiero a que no hay ningún elemento en array1 que no esté contenido en array2.

Se podría utilizar aplanar () y la diferencia () para esto, que funciona bien si no le importa si hay artículos en array2que no están en array1. Parece que estás preguntando si array1 es un subconjunto de array2 .

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];

function isSubset(source, target) {
    return !_.difference(_.flatten(source), _.flatten(target)).length;
}

isSubset(array1, array2); // → true
array1.push('d');
isSubset(array1, array2); // → false
isSubset(array2, array1); // → true
Adam Boduch
fuente
4

Ya hay respuestas aquí, pero aquí está mi implementación JS pura. No estoy seguro de si es óptimo, pero seguro que es transparente, legible y simple.

// Does array a contain elements of array b?
const contains = (a, b) => new Set([...a, ...b]).size === a.length
const isEqualSet = (a, b) => contains(a, b) && contains(b, a)

La razón contains()es que si acontiene todos los elementos de b, ponerlos en el mismo conjunto no cambiaría el tamaño.

Por ejemplo, si const a = [1,2,3,4]y const b = [1,2], entonces new Set([...a, ...b]) === {1,2,3,4}. Como puede ver, el conjunto resultante tiene los mismos elementos quea .

A partir de ahí, para hacerlo más conciso, podemos resumirlo en lo siguiente:

const isEqualSet = (a, b) => {
  const unionSize = new Set([...a, ...b])
  return unionSize === a.length && unionSize === b.length
}
J.Ko
fuente
1

PURE JS (funciona también cuando las matrices y submatrices tienen más de 2 elementos con un orden arbitrario). Si strings contiene ,use como join('-')carácter de parámetro (puede ser utf) que no se usa en strings

array1.map(x=>x.sort()).sort().join() === array2.map(x=>x.sort()).sort().join()

Kamil Kiełczewski
fuente
0

Podemos usar la _.differencefunción para ver si hay alguna diferencia o no.

function isSame(arrayOne, arrayTwo) {
   var a = _.unique(arrayOne),
       b = _.unique(arrayTwo);

   if (a.length <= b.length) {
      a = arrayTwo;
      b = arrayOne;
      return _.isEmpty(_.difference(a.sort(), b.sort()));
   } else {
      return false;
   }

}

// examples
console.log(isSame([1, 2, 3], [1, 2, 3])); // true
console.log(isSame([1, 2, 4], [1, 2, 3])); // false
console.log(isSame([1, 2], [2, 3, 1])); // false
console.log(isSame([2, 3, 1], [1, 2])); // false

// Test cases pointed by Mariano Desanze, Thanks.
console.log(isSame([1, 2, 3], [1, 2, 2])); // false
console.log(isSame([1, 2, 2], [1, 2, 2])); // true
console.log(isSame([1, 2, 2], [1, 2, 3])); // false

Espero que esto ayude.

Amitesh
fuente
5
Incorrecto, su función se volverá trueparaconsole.log(isSame([1,2], [2,3,1]));
David Lin
3
Gracias @DavidLin por señalarlo. He realizado los cambios para considerar ese caso. Gracias y disculpa las molestias.
Amitesh
2
no es necesario cambiar de lugar para a & b si las longitudes no son las mismas. Si las longitudes difieren, entonces ya no pueden ser iguales, por lo que si (arrayOne.lenght! == arrayTwo.lenght) return false;
Alex
1
-1 No tiene sentido tener esas ay bvariables. Usted sólo se utiliza esas variables dentro de la if-thenparte, y la primera cosa que hacer Hay descartar aquellos valores cargados en la línea 2. Creo que la siguiente línea funcionará exactamente el mismo: return arrayOne.length <= arrayTwo.length && _.isEmpty(_.difference(arrayTwo.sort(), arrayTwo.sort());. Y <=también se puede mejorar en ===.
Mariano Desanze
1
Y _.differencedevolverá los elementos faltantes del primer argumento, pero no los elementos faltantes del segundo. Así que esto devolverá de forma incorrecta truecuando se repita el 1 de artículos en 2ª: isSame([1, 2, 3], [1, 2, 2]).
Mariano Desanze
0

Editar: me perdí el aspecto multidimensional de esta pregunta, así que lo dejo aquí en caso de que ayude a las personas a comparar matrices unidimensionales

Es una vieja pregunta, pero estaba teniendo problemas con la velocidad de uso de .sort()o sortBy(), así que usé esto en su lugar:

function arraysContainSameStrings(array1: string[], array2: string[]): boolean {
  return (
    array1.length === array2.length &&
    array1.every((str) => array2.includes(str)) &&
    array2.every((str) => array1.includes(str))
  )
}

Estaba destinado a fallar rápido y, para mis propósitos, funciona bien.

charliematters
fuente
¿Es realmente necesario el último control? array1.every((str) => array2.includes(str))debería ser suficiente. Además, el OP quería usar lodash, al menos debería decir por qué propone una solución vanillaJS (... ahora que tenemos todos e incluye ...). Proporcione también un ejemplo de cómo aplicar su función al problema proporcionado (matrices bidimensionales).
Line-o
Ese es un buen punto: no abordé el aspecto multidimensional, ni el requisito de lodash. Pensé que sería útil que cualquiera que busque (como yo lo hice) para lodash methods to compare arrays without considering orderver una alternativa en el Javascript moderno. La segunda verificación es necesaria ya que, de arraysContainSameStrings(['1', '2', '2'], ['1', '2', '3'])lo contrario, devolvería verdadero. Lo dejaré aquí, ya que podría ayudar, pero agradezco que no haya respondido a la pregunta
charliematters