Очікуйте, що масиви будуть рівними, ігноруючи порядок


85

За допомогою 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

Відповіді:


60

Якщо це лише цілі числа або інші примітивні значення, ви можете 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
— Shmiddty

[10, 2, 1].sort() ---> [1, 10, 2]
— Шмідди,

7
@Shmiddty Я не бачу, як це має значення в цьому випадку. Поки порядок однаковий для обох масивів, це повинно бути нормально.
— Кольорова панда

1
Справедливий момент. Варто зазначити, що все ж sortвідбувається на місці. (він змінює екземпляр, за яким його називають)
— Шміддті,

1
Частина об’єкта цієї відповіді насправді не перевірить, що об’єкти збігаються, оскільки це лише порівняння відображених масивів. Вам не потрібна карта, вона sortбере додаткову функцію, яку вона може використовувати для порівняння.
— стрункі

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

Ви можете використовувати awa.arrayContaining (array) зі стандартної шутки:

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

2

Жарт-продовжено надає нам кілька тверджень для спрощення наших тестів, він менш детальний, а для невдалих тестів помилка є більш явною.

У цьому випадку ми можемо використовувати 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] неправильно повернуть true)

[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

1
Помилки для цих масивів: [1,1,2,3], [3,3,1,2].
— Torben Kohlmeier

1
@TorbenKohlmeier Дякую, я оновив свою відповідь (визнаючи поразку щодо не унікальних масивів)
— Домі


-1

Ви можете використовувати щось на зразок:

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

Запам’ятайте імпорт jasmine. Або додайте його до свого.eslintrc


-3

Jest має функцію під назвою, expect.arrayContainingяка буде робити саме те, що ви хочете:

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

Ви можете перевірити, чи мають вони однакову довжину, оскільки тест пройде if

очікуваний масив - це підмножина отриманого масиву

згідно з док.

EDIT: Вибачте, що я не помітив тегу жасмину, це спосіб, який працює з Jest

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.