Ожидайте, что массивы будут равны, игнорируя порядок

86

С помощью Jasmine есть ли способ проверить, содержат ли 2 массива одинаковые элементы, но не обязательно в одном порядке? т.е.

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

expect(array1).toEqualIgnoreOrder(array2);//should be true
Дэвид говорит: "Восстановить Монику"
источник
23
expect(array1.sort()).toEqual(array2.sort());?
raina77ow
@ raina77ow Думаю, это тоже сработает.
Дэвид говорит: "Восстановите Монику"
1
Стоит ли мне дать ответ?
raina77ow
1
@ raina77ow Когда это массив объектов, все становится немного сложнее. Было бы неплохо, если бы у Жасмин было что-то нестандартное для этого.
Дэвид говорит: "Восстановите Монику"
2
Я не нашел ничего отличного в самом жасмине, поэтому фактически ввел lodash (или вы могли использовать подчеркивание / другую библиотеку коллекции js) в свой тестовый проект для таких вещей.
ktharsis

Ответы:

61

Если это просто целые числа или другие примитивные значения, вы можете sort()их перед сравнением.

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

Если его объекты, объедините его с map()функцией, чтобы извлечь идентификатор, который будет сравниваться

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());
Цветная панда
источник
метод сортировки массива по умолчанию использует сравнение строк для чисел. "10" < "2" === true
Шмиддти
[10, 2, 1].sort() ---> [1, 10, 2]
Шмиддти
7
@Shmiddty Я не понимаю, какое это имеет значение в данном случае. Пока порядок для обоих массивов одинаков, все должно быть в порядке.
Цветная панда
1
Честная оценка. Однако стоит отметить, что это sortпроисходит на месте. (он мутирует экземпляр, на котором он вызван)
Шмиддти
1
Объектная часть этого ответа на самом деле не проверяет соответствие объектов, поскольку она сравнивает только сопоставленные массивы. Карта вам не нужна, sortона может использовать дополнительную функцию для сравнения.
Slifty
19

жасмин версии 2.8 и более поздних имеет

jasmine.arrayWithExactContents()

Это предполагает, что массив содержит точно перечисленные элементы в любом порядке.

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

См. Https://jasmine.github.io/api/3.4/jasmine.html

кексмаста
источник
13

просто...

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

expect(array1).toEqual(jasmine.arrayContaining(array2));
ПрофиПрог
источник
7
Хороший ответ! Вам также необходимо проверить, что длины равны, иначе вы получите ложное срабатывание на [1,2,3,4] и [3,2,1].
Кристиан
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
Используйте .forEachвместо, .mapчтобы сэкономить время и кучу памяти.
Darkhogg
1
К сожалению , это будет проходить со следующими массивами , даже если они разные: array1 = [1, 2],array2 = [1, 1]
redbmk
2
Хороший улов @redbmk Я добавил чек на это, спасибо!
Jannic Beck
Думаю, все еще проблема - а если массивы [1,1,2]и [1,2,2]? Может быть, использовать карту для каждого или что-то в этом роде? например, array1.reduce((map, item) => { map.set(item, (map.get(item) || 0) + 1)), new Map())для обоих массивов, затем прокрутите их и проверьте, совпадают ли суммы? Кажется, много итераций, но было бы более тщательно.
redbmk
Можно использовать исключения из массива элементов управления (удалить элемент, если он найден, затем длина проверки равна 0), но в обычных случаях это не стоит усилий.
lifecoder
4

Вы можете использовать expect.arrayContain (array) из стандартной шутки:

  const expected = ['Alice', 'Bob'];
  it('matches even if received contains additional elements', () => {
    expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
  });
Марина
источник
2

Пакет с расширением jest предоставляет нам несколько утверждений для упрощения наших тестов, он менее подробен, а для неудачных тестов ошибка более явна.

В этом случае мы могли бы использовать toIncludeSameMembers

expect([{foo: "bar"}, {baz: "qux"}]).toIncludeSameMembers([{baz: "qux"}, {foo: "bar"}]);
dave008
источник
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
    })
}

Идея здесь состоит в том, чтобы сначала определить, одинаковы ли длины двух массивов, а затем проверить, все ли элементы находятся в другом массиве.

SkuraZZ
источник
При этом не учитывается частота equal([1, 1, 2], [1, 2, 2])возвращений true.
MarkMYoung
0

Вот решение, которое будет работать для любого числа или массивов

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

Некоторые тесты (несколько ответов на этот вопрос не учитывают массивы с несколькими элементами одного и того же значения, поэтому [1, 2, 2] и [1, 2] будут неправильно возвращать истину)

[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

Этот алгоритм отлично подходит для массивов, где каждый элемент уникален. Если нет, вы можете добавить что-нибудь для проверки дубликатов ...

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

Этот подход имеет худшую теоретическую производительность в худшем случае во время выполнения, но, поскольку он не выполняет никаких операций записи в массив, во многих случаях он может быть быстрее (производительность еще не проверена):

ПРЕДУПРЕЖДЕНИЕ. Как отметил Торбен в комментариях, этот подход работает только в том случае, если оба массива имеют уникальные (неповторяющиеся) элементы (как и некоторые другие ответы здесь).

/**
 * 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
Что вы имеете в виду, когда говорите, что он создает множество копий? Array#sortсортирует массивы на месте.
philraj 06
1
Не работает для этих массивов: [1,1,2,3], [3,3,1,2].
Торбен Кольмайер
1
@TorbenKohlmeier Спасибо, я обновил свой ответ (признавая поражение в отношении неуникальных массивов)
Доми
0

В настоящее время для этого СЛУЧАЯ ИСПОЛЬЗОВАНИЯ существует сопоставитель:

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' }]);
});
Даниэль Мальдонадо
источник
-1

Вы можете использовать что-то вроде:

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

Помните импорт jasmine. Или добавьте его в свой.eslintrc

КВиин Койой
источник
-3

У Jest есть функция, expect.arrayContainingкоторая будет делать именно то, что вы хотите:

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

вы можете проверить, одинаковой ли они длины, так как тест пройдёт, если

ожидаемый массив - это подмножество полученного массива

согласно док.

РЕДАКТИРОВАТЬ: извините, что я не заметил тега жасмина, это способ работы с Jest

Дэвид Ли
источник