Erwarten Sie, dass Arrays gleich sind und die Reihenfolge ignorieren


91

Gibt es mit Jasmine eine Möglichkeit zu testen, ob 2 Arrays dieselben Elemente enthalten, aber nicht unbedingt in derselben Reihenfolge? dh

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

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

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

@ raina77ow Ich denke, das würde auch funktionieren.
David sagt Reinstate Monica

1
Soll ich das beantworten?
raina77ow

1
@ raina77ow Es wird etwas komplizierter, wenn es sich um eine Reihe von Objekten handelt. Es wäre schön, wenn Jasmine etwas aus der Box dafür hätte.
David sagt Reinstate Monica

2
Ich fand in Jasmin selbst nichts Großartiges, also führte ich lodash (oder Sie könnten Unterstrich / andere js-Sammlungsbibliothek verwenden) in mein Testprojekt für solche Dinge ein.
Ktharsis

Antworten:


63

Wenn es sich nur um Ganzzahlen oder andere primitive Werte handelt, können Sie sort()diese vor dem Vergleich verwenden.

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

Wenn es sich um Objekte handelt, kombinieren Sie es mit der map()Funktion, um einen zu vergleichenden Bezeichner zu extrahieren

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

Die Standard-Array-Sortiermethode verwendet den Zeichenfolgenvergleich für Zahlen. "10" < "2" === true
Shmiddty

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

9
@Shmiddty Ich sehe nicht, wie es in diesem Fall wichtig ist. Solange die Reihenfolge für beide Arrays gleich ist, sollte es in Ordnung sein.
Farbiger Panda

1
Gutes Argument. Es ist jedoch erwähnenswert, dass sortdies an Ort und Stelle geschieht. (es mutiert die Instanz, auf der es aufgerufen wird)
Shmiddty

1
Der Objektteil dieser Antwort überprüft nicht, ob die Objekte übereinstimmen, da nur die zugeordneten Arrays verglichen werden. Sie benötigen keine Karte, verwenden sorteine optionale Funktion, mit der der Vergleich durchgeführt werden kann.
Slifty

21

Jasmin Version 2.8 und höher hat

jasmine.arrayWithExactContents()

Was erwartet, dass ein Array genau die aufgelisteten Elemente in beliebiger Reihenfolge enthält.

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

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


12

einfach...

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

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

7
Gute Antwort! Sie müssen auch überprüfen, ob die Längen gleich sind, sonst haben Sie ein falsches Positiv für [1,2,3,4] und [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
Verwenden Sie .forEachstattdessen .map, um Zeit und Speicherplatz zu sparen.
Darkhogg

1
Leider wird dies auch mit den folgenden Arrays passieren , obwohl sie unterschiedlich sind: array1 = [1, 2],array2 = [1, 1]
redbmk

2
Netter Fang @redbmk Ich habe einen Scheck dafür hinzugefügt, danke!
Jannic Beck

Ich denke, es gibt immer noch ein Problem - was ist, wenn die Arrays [1,1,2]und sind [1,2,2]? Vielleicht eine Karte für jede oder etwas? zB array1.reduce((map, item) => { map.set(item, (map.get(item) || 0) + 1)), new Map())für beide Arrays, dann durchlaufen Sie sie und überprüfen Sie, ob die Beträge gleich sind? Scheint wie viele Iterationen, wäre aber gründlicher.
Redbmk

Ausschlüsse aus dem Steuerungsarray können verwendet werden (Element entfernen, wenn gefunden, dann überprüfen, ob die Länge am Ende 0 ist), aber es lohnt sich in normalen Fällen nicht.
Lifecoder

8

Sie könnten expected.arrayContaining (Array) aus Standard-Scherz verwenden:

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

2

Das Jest-Extended- Paket enthält nur wenige Aussagen zur Vereinfachung unserer Tests. Es ist weniger ausführlich und bei fehlgeschlagenen Tests ist der Fehler expliziter.

In diesem Fall könnten wir toIncludeSameMembers verwenden

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

Die Idee hier ist, zuerst festzustellen, ob die Länge der beiden Arrays gleich ist, und dann zu überprüfen, ob sich alle Elemente im Array des anderen befinden.


Dies berücksichtigt nicht die Häufigkeit von Artikeln: equal([1, 1, 2], [1, 2, 2])Rücksendungen true.
MarkMYoung

0

Hier ist eine Lösung, die für eine beliebige Anzahl oder Arrays funktioniert

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

Einige Tests (einige Antworten auf diese Frage berücksichtigen keine Arrays mit mehreren Elementen desselben Werts, sodass [1, 2, 2] und [1, 2] fälschlicherweise true zurückgeben würden)

[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

Dieser Algorithmus eignet sich hervorragend für Arrays, bei denen jedes Element ein Unikat ist. Wenn nicht, können Sie etwas hinzufügen, um nach Duplikaten zu suchen ...

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

Dieser Ansatz weist eine schlechtere theoretische Worst-Case-Laufzeitleistung auf. Da jedoch keine Schreibvorgänge auf dem Array ausgeführt werden, ist er unter vielen Umständen möglicherweise schneller (die Leistung wurde noch nicht getestet):

WARNUNG: Wie Torben in den Kommentaren hervorhob, funktioniert dieser Ansatz nur, wenn beide Arrays eindeutige (sich nicht wiederholende) Elemente haben (genau wie einige der anderen Antworten hier).

/**
 * 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
Was meinst du, wenn du sagst, dass es viele Kopien erstellt? Array#sortsortiert Arrays an Ort und Stelle.
Philraj

1
Schlägt für diese Arrays fehl: [1,1,2,3], [3,3,1,2].
Torben Kohlmeier

1
@TorbenKohlmeier Danke, ich habe meine Antwort aktualisiert (ich gebe eine Niederlage in Bezug auf nicht eindeutige Arrays zu)
Domi


-1

Sie könnten etwas verwenden wie:

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

Denken Sie daran, importieren jasmine. Oder fügen Sie es Ihrem hinzu.eslintrc


-4

Jest hat eine Funktion namens, expect.arrayContainingdie genau das tut, was Sie wollen:

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

Möglicherweise möchten Sie auch überprüfen, ob sie dieselbe Länge haben, da der Test bestanden wird, wenn

Das erwartete Array ist eine Teilmenge des empfangenen Arrays

nach dem doc.

EDIT: Tut mir leid, dass ich das Jasmin-Tag nicht bemerkt habe. Dies ist eine Methode, die mit Jest funktioniert

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.