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
David diz Reintegrar Monica
fonte
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());
Panda colorido
fonte
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

Keksmasta
fonte
13

simples...

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

expect(array1).toEqual(jasmine.arrayContaining(array2));
ProfiProg
fonte
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)
Jannic Beck
fonte
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));
  });
Marina
fonte
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"}]);
dave008
fonte
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;
}
Ravi
fonte
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.

SkuraZZ
fonte
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
Tyler
fonte
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;
}

Joe DF
fonte
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>

Domi
fonte
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
0

Atualmente existe uma correspondência para este CASO DE USO:

https://github.com/jest-community/jest-extended/pull/122/files

test('passes when arrays match in a different order', () => {
  expect([1, 2, 3]).toMatchArray([3, 1, 2]);
  expect([{ foo: 'bar' }, { baz: 'qux' }]).toMatchArray([{ baz: 'qux' }, { foo: 'bar' }]);
});
Daniel Maldonado
fonte
Mas isso faz parte jest-extended, ou seja, não está disponível como uma funcionalidade central do Jest, certo?
bluenote10
-1

Você poderia usar algo como:

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

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

KViin Coyoy
fonte
-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

David Lee
fonte