Espere que las matrices sean iguales ignorando el orden


86

Con Jasmine, ¿hay alguna manera de probar si 2 matrices contienen los mismos elementos, pero no están necesariamente en el mismo orden? es decir

array1 = [1,2,3];
array2 = [3,2,1];

expect(array1).toEqualIgnoreOrder(array2);//should be true

23
expect(array1.sort()).toEqual(array2.sort());?
raina77ow

@ raina77ow Supongo que eso también funcionaría.
David dice Reincorporar a Monica el

1
¿Debería hacer de esto una respuesta?
raina77ow

1
@ raina77ow Se vuelve un poco más complicado cuando se trata de una matriz de objetos. Sería bueno si Jasmine tuviera algo fuera de la caja para esto.
David dice Reincorporar a Monica el

2
No encontré nada bueno en el jazmín, así que introduje lodash (o podría usar subrayado / otra biblioteca de colección js) en mi proyecto de prueba para cosas como esta.
ktharsis

Respuestas:


61

Si son solo números enteros u otros valores primitivos, puede sort()usarlos antes de comparar.

expect(array1.sort()).toEqual(array2.sort());

Si sus objetos, combínelo con la map()función para extraer un identificador que se comparará

array1 = [{id:1}, {id:2}, {id:3}];
array2 = [{id:3}, {id:2}, {id:1}];

expect(array1.map(a => a.id).sort()).toEqual(array2.map(a => a.id).sort());

el método de ordenación de matrices predeterminado utiliza la comparación de cadenas para números. "10" < "2" === true
Shmiddty

[10, 2, 1].sort() ---> [1, 10, 2]
Shmiddty

7
@Shmiddty No veo qué importancia tiene en este caso. Siempre que el orden sea el mismo para ambas matrices, debería estar bien.
Panda de colores

1
Punto justo. Sin sortembargo, vale la pena señalar que eso ocurre en el lugar. (muta la instancia en la que se llama)
Shmiddty

1
La parte del objeto de esta respuesta en realidad no verificará que los objetos coincidan, ya que solo está comparando las matrices mapeadas. No necesita mapa, sorttoma una función opcional que puede usar para hacer la comparación.
Slifty

19

jasmine versión 2.8 y posterior tiene

jasmine.arrayWithExactContents()

Lo que espera que una matriz contenga exactamente los elementos enumerados, en cualquier orden.

array1 = [1,2,3];
array2 = [3,2,1];
expect(array1).toEqual(jasmine.arrayWithExactContents(array2))

Ver https://jasmine.github.io/api/3.4/jasmine.html


13

sencillo...

array1 = [1,2,3];
array2 = [3,2,1];

expect(array1).toEqual(jasmine.arrayContaining(array2));

7
¡Buena respuesta! También debe comprobar que las longitudes sean iguales; de lo contrario, obtendrá un falso positivo en [1,2,3,4] y [3,2,1].
Kristian Hanekamp

10
// check if every element of array2 is element of array1
// to ensure [1, 1] !== [1, 2]
array2.forEach(x => expect(array1).toContain(x))

// check if every element of array1 is element of array2
// to ensure [1, 2] !== [1, 1]
array1.forEach(x => expect(array2).toContain(x))

// check if they have equal length to ensure [1] !== [1, 1]
expect(array1.length).toBe(array2.length)

2
Úselo en .forEachlugar de .mappara ahorrar tiempo y mucha memoria.
Darkhogg

1
Desafortunadamente esto pasará con las siguientes matrices a pesar de que son diferentes: array1 = [1, 2],array2 = [1, 1]
redbmk

2
Buena captura @redbmk. Agregué un cheque para esto, ¡gracias!
Jannic Beck

Creo que todavía hay un problema: ¿qué pasa si las matrices son [1,1,2]y [1,2,2]? ¿Quizás usar un mapa para cada uno o algo así? por ejemplo, array1.reduce((map, item) => { map.set(item, (map.get(item) || 0) + 1)), new Map())para ambas matrices, luego recorrerlas y verificar que las cantidades sean las mismas. Parece que hay muchas iteraciones, pero sería más completo.
redbmk

Se pueden usar exclusiones de la matriz de control (elimine el elemento cuando se encuentre, luego verifique que la longitud sea 0 al final), pero no vale la pena el esfuerzo en casos regulares.
Lifecoder

4

Puede usar wait.arrayContaining (matriz) de broma estándar:

  const expected = ['Alice', 'Bob'];
  it('matches even if received contains additional elements', () => {
    expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
  });

2

El paquete jest-extended nos proporciona pocas afirmaciones para simplificar nuestras pruebas, es menos detallado y para las pruebas fallidas, el error es más explícito.

Para este caso podríamos utilizar toIncludeSameMembers

expect([{foo: "bar"}, {baz: "qux"}]).toIncludeSameMembers([{baz: "qux"}, {foo: "bar"}]);

1
//Compare arrays without order
//Example
//a1 = [1, 2, 3, 4, 5]
//a2 = [3, 2, 1, 5, 4]
//isEqual(a1, a2) -> true
//a1 = [1, 2, 3, 4, 5];
//a2 = [3, 2, 1, 5, 4, 6];
//isEqual(a1, a2) -> false


function isInArray(a, e) {
  for ( var i = a.length; i--; ) {
    if ( a[i] === e ) return true;
  }
  return false;
}

function isEqArrays(a1, a2) {
  if ( a1.length !== a2.length ) {
    return false;
  }
  for ( var i = a1.length; i--; ) {
    if ( !isInArray( a2, a1[i] ) ) {
      return false;
    }
  }
  return true;
}

0
function equal(arr1, arr2){
    return arr1.length === arr2.length
    &&
    arr1.every((item)=>{
        return arr2.indexOf(item) >-1
    }) 
    &&
    arr2.every((item)=>{
        return arr1.indexOf(item) >-1
    })
}

La idea aquí es determinar primero si la longitud de las dos matrices es la misma, luego verificar si todos los elementos están en la matriz de la otra.


Esto no tiene en cuenta la frecuencia de los artículos: equal([1, 1, 2], [1, 2, 2])devoluciones true.
MarkMYoung

0

Aquí hay una solución que funcionará para cualquier número o matriz.

https://gist.github.com/tvler/cc5b2a3f01543e1658b25ca567c078e4

const areUnsortedArraysEqual = (...arrs) =>
  arrs.every((arr, i, [first]) => !i || arr.length === first.length) &&
  arrs
    .map(arr =>
      arr.reduce(
        (map, item) => map.set(item, (map.get(item) || 0) + 1),
        new Map(),
      ),
    )
    .every(
      (map, i, [first]) =>
        !i ||
        [...first, ...map].every(([item]) => first.get(item) === map.get(item)),
    );

Algunas pruebas (algunas respuestas a esta pregunta no tienen en cuenta las matrices con varios elementos del mismo valor, por lo que [1, 2, 2] y [1, 2] devolverían verdadero incorrectamente)

[1, 2] true
[1, 2], [1, 2] true
[1, 2], [1, 2], [1, 2] true
[1, 2], [2, 1] true
[1, 1, 2], [1, 2, 1] true
[1, 2], [1, 2, 3] false
[1, 2, 3, 4], [1, 2, 3], [1, 2] false
[1, 2, 2], [1, 2] false
[1, 1, 2], [1, 2, 2] false
[1, 2, 3], [1, 2], [1, 2, 3] false

0

Este algoritmo es ideal para matrices en las que cada elemento es único. Si no es así, puede agregar algo para verificar si hay duplicados ...

tests = [
  [ [1,0,1] , [0,1,1] ],
  [ [1,0,1] , [0,0,1] ], //breaks on this one...
  [ [2,3,3] , [2,2,3] ], //breaks on this one also...
  [ [1,2,3] , [2,1,3] ],
  [ [2,3,1] , [1,2,2] ],
  [ [2,2,1] , [1,3,2] ]
]

tests.forEach(function(test) {
  console.log('eqArraySets( '+test[0]+' , '+test[1]+' ) = '+eqArraySets( test[0] , test[1] ));
});


function eqArraySets(a, b) {
	if ( a.length !== b.length ) { return false; }
	for ( var i = a.length; i--; ) {
		if ( !(b.indexOf(a[i])>-1) ) { return false; }
		if ( !(a.indexOf(b[i])>-1) ) { return false; }
	}
	return true;
}


0

Este enfoque tiene un peor rendimiento teórico en el peor de los casos en tiempo de ejecución, pero, debido a que no realiza ninguna escritura en la matriz, podría ser más rápido en muchas circunstancias (aún no se ha probado el rendimiento):

ADVERTENCIA: Como señaló Torben en los comentarios, este enfoque solo funciona si ambas matrices tienen elementos únicos (no repetidos) (al igual que varias de las otras respuestas aquí).

/**
 * Determine whether two arrays contain exactly the same elements, independent of order.
 * @see /programming/32103252/expect-arrays-to-be-equal-ignoring-order/48973444#48973444
 */
function cmpIgnoreOrder(a, b) {
  const { every, includes } = _;
  return a.length === b.length && every(a, v => includes(b, v));
}

// the following should be all true!
const results = [
  !!cmpIgnoreOrder([1,2,3], [3,1,2]),
  !!cmpIgnoreOrder([4,1,2,3], [3,4,1,2]),
  !!cmpIgnoreOrder([], []),
  !cmpIgnoreOrder([1,2,3], [3,4,1,2]),
  !cmpIgnoreOrder([1], []),
  !cmpIgnoreOrder([1, 3, 4], [3,4,5])
];

console.log('Results: ', results)
console.assert(_.reduce(results, (a, b) => a && b, true), 'Test did not pass!');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>


1
¿A qué te refieres cuando dices que crea muchas copias? Array#sortordena las matrices en el lugar.
philraj

1
Falla para estas matrices: [1,1,2,3], [3,3,1,2].
Torben Kohlmeier

1
@TorbenKohlmeier Gracias, actualicé mi respuesta (admitiendo la derrota con respecto a las matrices no únicas)
Domi


-1

Podrías usar algo como:

expect(array1).toEqual(jasmine.arrayContaining(array2));

Recuerde importar jasmine. O agréguelo a su.eslintrc


-3

Jest tiene una función llamada expect.arrayContainingque hará exactamente lo que quieras:

expect(array1).toEqual(expect.arrayContaining(array2))

es posible que desee verificar si también tienen la misma longitud, ya que la prueba pasará si

la matriz esperada es un subconjunto de la matriz recibida

según el doc.

EDITAR: Lo siento, no me di cuenta de la etiqueta de jazmín, esta es una forma en que funciona con Jest

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.