期望 Array 等于忽略顺序

对于 Jasmine,有没有一种方法可以测试两个数组是否包含相同的元素,但不一定是按照相同的顺序?也就是说

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


expect(array1).toEqualIgnoreOrder(array2);//should be true
99072 次浏览
//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;
}

剪辑

Jasmine 2.8添加了 arrayWithExactContents,如果实际值是以任意顺序包含示例中所有元素的 Array,则 arrayWithExactContents将成功。

参见 Keksmasta 的回答


原始(过时)答案

如果只是整数或其他基元值,可以在比较之前对它们进行 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());

这种方法在理论上的最坏情况下运行时性能较差,但是,由于它不对数组执行任何写操作,所以在许多情况下它可能更快(尚未测试性能) :

警告: 正如 Torben 在评论中指出的,这种方法只有在两个数组都有唯一(非重复)元素时才有效(就像这里的其他几个答案一样)。

/**
* Determine whether two arrays contain exactly the same elements, independent of order.
* @see https://stackoverflow.com/questions/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>

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

这里的想法是首先确定两个数组的长度是否相同,然后检查是否所有元素都在另一个数组中。

这里有一个适用于任何数字或数组的解决方案

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

这种算法对于每个条目都是唯一的数组非常有用。如果没有,你可以添加一些东西来检查重复..。

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

简单。

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


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

目前这个用例有一个匹配器:

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

Jasmine 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

开玩笑的包提供了一些断言来简化我们的测试,它没有那么冗长,对于失败的测试,错误更加明显。

在这种情况下,我们可以使用 包括相同的成员

expect([{foo: "bar"}, {baz: "qux"}]).toIncludeSameMembers([{baz: "qux"}, {foo: "bar"}]);

您可以使用来自标准 jest:

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

我当前正在使用这个 helper 函数(对于 TypeScript)。它还确保支持具有非唯一元素的数组。

function expectArraysToBeEqualIgnoringOrder<T>(arr1: T[], arr2: T[]) {


while(arr1.length > 0) {


expect(arr1.length).toEqual(arr2.length)


const elementToDelete = arr1[0]


arr1 = arr1.filter(element => element !== elementToDelete)
arr2 = arr2.filter(element => element !== elementToDelete)


}


expect(arr2.length).toEqual(0)


}

其他许多答案并不能正确处理这样的案例:

array1: [a, b, b, c]
array2: [a, b, c, c]

这里两个数组中的元素数量相同,两个数组都包含来自另一个数组的所有元素,但它们是不同的数组,测试应该会失败。 它运行在 O (n ^ 2)(精确地说是(n ^ 2 + n)/2)中,所以它不适合非常大的数组,但是它适合不容易排序的数组,因此不能在 O (n * log (n))中进行比较

expect.arrayContaining(['two', 'one', 'three'])