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


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"}]);

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

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


При этом не учитывается частота 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 Спасибо, я обновил свой ответ (признавая поражение в отношении неуникальных массивов)
Доми


-1

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

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

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


-3

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

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

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.