Esperar que as matrizes sejam iguais, ignorando a ordem


93

Com o Jasmine, há uma maneira de testar se 2 arrays contêm os mesmos elementos, mas não estão necessariamente na mesma ordem? ie

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

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

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

@ raina77ow Acho que também funcionaria.
David diz Restabelecer Monica de

1
Devo fazer disso uma resposta?
raina77ow

1
@ raina77ow Fica um pouco mais complicado quando é um array de objetos. Seria bom se Jasmine tivesse algo fora da caixa para isso.
David diz Restabelecer Monica de

2
Não achei nada ótimo no próprio jasmine, então introduzi o lodash (ou você pode usar o sublinhado / outra biblioteca de coleção js) em meu projeto de teste para coisas como essa.
ktharsis

Respostas:


65

Se forem apenas números inteiros ou outros valores primitivos, você pode sort()colocá-los antes de comparar.

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

Se forem objetos, combine-o com a map()função para extrair um identificador que será comparado

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

o método de classificação de array padrão usa comparação de strings para números. "10" < "2" === true
Shmiddty

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

9
@Shmiddty Não vejo como isso importa neste caso. Contanto que a ordem seja a mesma para as duas matrizes, não haverá problema.
Panda colorido de

1
Ponto justo. É importante notar que isso sortacontece no local, no entanto. (muda a instância na qual é chamado)
Shmiddty

1
A parte do objeto desta resposta não verificará realmente se os objetos correspondem, uma vez que está apenas comparando os arrays mapeados. Você não precisa do mapa, sorttem uma função opcional que pode usar para fazer a comparação.
fraco

22

jasmine versão 2.8 e posterior tem

jasmine.arrayWithExactContents()

Que espera que uma matriz contenha exatamente os elementos listados, em qualquer ordem.

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

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


13

simples...

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

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

7
Boa resposta! Você também precisa verificar se os comprimentos são iguais, caso contrário, você terá um falso positivo em [1,2,3,4] e [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
Use em .forEachvez de .mappara economizar algum tempo e muita memória.
Darkhogg

1
Infelizmente isso vai passar com os seguintes matrizes, mesmo que eles são diferentes: array1 = [1, 2],array2 = [1, 1]
redbmk

2
Boa captura @redbmk Eu adicionei um cheque para isso, obrigado!
Jannic Beck

Acho que ainda há um problema - e se os arrays forem [1,1,2]e [1,2,2]? Talvez usando um mapa para cada um ou algo assim? por exemplo, array1.reduce((map, item) => { map.set(item, (map.get(item) || 0) + 1)), new Map())para ambas as matrizes, então faça um loop através delas e verifique se os valores são iguais Parece muitas iterações, mas seria mais completo.
redbmk

Exclusões do array de controle podem ser usadas (remova o elemento quando encontrado, então verifique o comprimento é 0 no final), mas não vale o esforço em casos regulares.
Lifecoder

10

Você pode usar expect.arrayContaining (array) do jest padrão:

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

Solução perfeita! Esta deve ser a resposta selecionada.
rohit_wason

2

O pacote jest-extended fornece-nos poucas afirmações para simplificar nossos testes, é menos prolixo e para testes que falham o erro é mais explícito.

Para este caso, poderíamos usar 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
    })
}

A ideia aqui é primeiro determinar se o comprimento das duas matrizes é o mesmo e, em seguida, verificar se todos os elementos estão na outra matriz.


Isso não leva em consideração a frequência dos itens: equal([1, 1, 2], [1, 2, 2])devoluções true.
MarkMYoung

0

Aqui está uma solução que funcionará para qualquer número ou matrizes

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

Alguns testes (algumas respostas a esta pergunta não levam em consideração matrizes com vários itens do mesmo valor, então [1, 2, 2] e [1, 2] retornariam incorretamente verdadeiro)

[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

Esse algoritmo é ótimo para matrizes em que cada item é único. Se não, você pode adicionar algo para verificar se há duplicatas ...

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

Esta abordagem tem pior desempenho de tempo de execução teórico de pior caso, mas, como não executa nenhuma gravação na matriz, pode ser mais rápida em muitas circunstâncias (ainda não testei o desempenho):

AVISO: Como Torben apontou nos comentários, essa abordagem só funciona se ambos os arrays tiverem elementos exclusivos (não repetitivos) (assim como várias das outras respostas aqui).

/**
 * 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
O que você quer dizer quando diz que cria muitas cópias? Array#sortclassifica matrizes no local.
philraj

1
Falha para essas matrizes: [1,1,2,3], [3,3,1,2].
Torben Kohlmeier

1
@TorbenKohlmeier Obrigado, atualizei minha resposta (admitindo a derrota em relação a arrays não exclusivos)
Domi


-1

Você poderia usar algo como:

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

Lembre-se de importar jasmine. Ou adicione ao seu.eslintrc


-4

Jest tem uma função chamada expect.arrayContainingque fará exatamente o que você quiser:

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

você pode querer verificar se eles têm o mesmo comprimento também, uma vez que o teste passará se

a matriz esperada é um subconjunto da matriz recebida

de acordo com o doc.

EDIT: Desculpe não ter notado a etiqueta de jasmim, é uma forma que funciona com Jest

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.