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


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.