Aspettatevi che gli array siano uguali ignorando l'ordine


95

Con Jasmine c'è un modo per verificare se 2 array contengono gli stessi elementi, ma non sono necessariamente nello stesso ordine? cioè

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

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

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

@ raina77ow immagino che funzionerebbe anche questo.
David dice Reinstate Monica

1
Devo renderlo una risposta?
raina77ow

1
@ raina77ow Diventa un po 'più complicato quando è un array di oggetti. Sarebbe bello se Jasmine avesse qualcosa fuori dagli schemi per questo.
David dice Reinstate Monica

2
Non ho trovato nulla di eccezionale in jasmine stesso, quindi ho effettivamente introdotto lodash (o potresti usare il carattere di sottolineatura / altra libreria di raccolta js) nel mio progetto di prova per cose come questa.
ktharsis

Risposte:


65

Se sono solo numeri interi o altri valori primitivi, puoi sort()inserirli prima del confronto.

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

Se i suoi oggetti, combinalo con la map()funzione per estrarre un identificatore che verrà confrontato

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

il metodo di ordinamento dell'array predefinito utilizza il confronto tra stringhe per i numeri. "10" < "2" === true
Shmiddty

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

9
@Shmiddty Non vedo come sia importante in questo caso. Finché l'ordine è lo stesso per entrambi gli array, dovrebbe andare bene.
Panda colorato

1
Punto valido. Vale la pena notare che ciò sortaccade sul posto, però. (muta l'istanza su cui è chiamato)
Shmiddty

1
La parte dell'oggetto di questa risposta non verificherà effettivamente che gli oggetti corrispondano, poiché sta solo confrontando gli array mappati. Non hai bisogno di map, sortprende una funzione opzionale che può usare per fare il confronto.
slifty

22

jasmine versione 2.8 e successive ha

jasmine.arrayWithExactContents()

Il che prevede che un array contenga esattamente gli elementi elencati, in qualsiasi ordine.

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

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


13

semplice...

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

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

7
Bella risposta! Devi anche controllare che le lunghezze siano uguali, altrimenti avrai un falso positivo su [1,2,3,4] e [3,2,1].
Kristian Hanekamp

11

Potresti usare wait.arrayContain (array) dallo standard jest:

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

Soluzione perfetta! Questa dovrebbe essere la risposta selezionata ..
rohit_wason

questa sembra essere la risposta migliore.
cevaris

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
Utilizzare .forEachinvece di .mapper risparmiare tempo e un po 'di memoria.
Darkhogg

1
Purtroppo questo passerà con le seguenti matrici anche se sono differenti: array1 = [1, 2],array2 = [1, 1]
redbmk

2
Bella cattura @redbmk ho aggiunto un assegno per questo, grazie!
Jannic Beck

Penso che ci sia ancora un problema: cosa succede se gli array sono [1,1,2]e [1,2,2]? Forse usando una mappa per ognuno o qualcosa del genere? ad es. array1.reduce((map, item) => { map.set(item, (map.get(item) || 0) + 1)), new Map())per entrambi gli array, quindi passare in rassegna e verificare che gli importi siano gli stessi? Sembra che ci siano molte iterazioni ma sarebbe più approfondito.
redbmk

È possibile utilizzare esclusioni dall'array di controllo (rimuovere l'elemento quando viene trovato, quindi verificare che la lunghezza sia 0 alla fine), ma nei casi normali non vale la pena.
lifecoder

2

Il pacchetto jest-extended ci fornisce poche asserzioni per semplificare i nostri test, è meno dettagliato e per i test falliti l'errore è più esplicito.

In questo caso potremmo usare 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
    })
}

L'idea qui è di determinare prima se la lunghezza dei due array è la stessa, quindi controllare se tutti gli elementi sono nell'array dell'altro.


Questo non tiene conto della frequenza degli articoli: equal([1, 1, 2], [1, 2, 2])resi true.
MarkMYoung

0

Ecco una soluzione che funzionerà per qualsiasi numero o array

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

Alcuni test (alcune risposte a questa domanda non tengono conto di array con più elementi dello stesso valore, quindi [1, 2, 2] e [1, 2] restituirebbero erroneamente true)

[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

Questo algoritmo è ottimo per gli array in cui ogni elemento è unico. In caso contrario, puoi aggiungere qualcosa per verificare la presenza di duplicati ...

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

Questo approccio ha prestazioni di runtime nel caso peggiore teorico peggiore, ma, poiché non esegue alcuna scrittura sull'array, potrebbe essere più veloce in molte circostanze (prestazioni non ancora testate):

ATTENZIONE: come Torben ha sottolineato nei commenti, questo approccio funziona solo se entrambi gli array hanno elementi unici (non ripetitivi) (proprio come molte altre risposte qui).

/**
 * 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
Cosa intendi quando dici che crea molte copie? Array#sortordina gli array sul posto.
philraj

1
Non riesce per questi array: [1,1,2,3], [3,3,1,2].
Torben Kohlmeier

1
@TorbenKohlmeier Grazie, ho aggiornato la mia risposta (ammettendo la sconfitta per quanto riguarda gli array non univoci)
Domi


-1

Potresti usare qualcosa come:

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

Ricorda importazione jasmine. Oppure aggiungilo al tuo file.eslintrc


-4

Jest ha una funzione chiamata expect.arrayContainingche farà esattamente quello che vuoi:

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

potresti voler controllare se hanno anche la stessa lunghezza, poiché il test passerà se

l'array atteso è un sottoinsieme dell'array ricevuto

secondo il doc.

EDIT: Mi dispiace di non aver notato il tag jasmine, questo è un modo che funziona con Jest

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.