期望数组相等,忽略顺序


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 2015年

1
@ raina77ow当它包含对象数组时,它会变得有些复杂。如果茉莉花开箱即用,那就太好了。
大卫说恢复莫妮卡

2
我没有在茉莉花中找到任何很棒的东西,所以实际上将lodash(或者您可以使用下划线/其他js集合库)引入我的测试项目中来进行类似的事情。
ktharsis

Answers:


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发生在原地。(它会改变其实例)
Shmiddty

1
该答案的对象部分实际上不会验证对象是否匹配,因为它只是比较映射的数组。您不需要map,sort它具有一个可选函数,可用于进行比较。
粗暴的


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]and[1,2,2]怎么办?也许为每个地图或某物使用地图?例如array1.reduce((map, item) => { map.set(item, (map.get(item) || 0) + 1)), new Map())对于两个数组,然后遍历它们并检查数量是否相同?看起来像很多迭代,但是会更彻底。
redbmk

可以使用控制数组中的排除项(找到时删除元素,最后检查长度为0),但是在常规情况下不值得这样做。
lifecoder '19

4

您可以使用标准玩笑中的Expect.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])return 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

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

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

/**
 * 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谢谢,我更新了我的答案(承认关于非唯一数组的失败)
Domi


-1

您可以使用类似:

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

记住导入jasmine。或将其添加到您的.eslintrc


-3

Jest有一个名为的函数expect.arrayContaining,它将完全满足您的要求:

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

您可能还想检查它们是否具有相同的长度,因为如果

预期数组是接收到的数组的子集

根据文档。

编辑:对不起,我没有注意到茉莉花标记,这是与杰斯特一起工作的一种方式

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.