順序を無視して配列が等しいことを期待する


94

Jasmineを使用して、2つの配列に同じ要素が含まれているかどうかをテストする方法はありますが、必ずしも同じ順序である必要はありませんか?すなわち

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

expect(array1).toEqualIgnoreOrder(array2);//should be true

24
expect(array1.sort()).toEqual(array2.sort());
raina77ow 2015

@ raina77ow私はそれもうまくいくと思います。
デビッドはモニカを復活させると言います2015

1
私はこれを答えるべきですか?
raina77ow 2015

1
@ raina77owオブジェクトの配列の場合、少し複雑になります。ジャスミンがこのために箱から出して何かを持っていればいいでしょう。
デビッドはモニカを復活させると言います2015

2
ジャスミン自体に素晴らしいものは何も見つからなかったので、実際にlodashをテストプロジェクトに導入しました(またはアンダースコア/他のjsコレクションライブラリを使用できます)。
ktharsis 2015

回答:


65

整数またはその他のプリミティブ値の場合は、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 2018年

[10, 2, 1].sort() ---> [1, 10, 2]
Shmiddty 2018年

9
@Shmiddtyこの場合の重要性がわかりません。両方のアレイの順序が同じである限り、問題はありません。
カラーパンダ

1
フェアポイント。sortただし、その場で発生することは注目に値します。(呼び出されたインスタンスを変更します)
Shmiddty 2018年

1
この回答のオブジェクト部分は、マップされた配列を比較しているだけなので、オブジェクトが一致することを実際には検証しません。マップは必要ありませんsort。比較を行うために使用できるオプションの関数を使用します。
slifty


13

シンプル...

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

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

7
いい答えだ!また、長さが等しいことを確認する必要があります。そうでない場合、[1,2,3,4]と[3,2,1]で誤検知が発生します。
KristianHanekamp19年

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

1
残念ながら、これは次の配列では異なりますが、合格します。array1 = [1, 2]array2 = [1, 1]
redbmk19年

2
ナイスキャッチ@redbmkこれのチェックを追加しました、ありがとう!
JannicBeck19年

私はまだ問題があると思います-配列が[1,1,2]とである場合はどうなります[1,2,2]か?たぶん、それぞれにマップを使用していますか?たとえばarray1.reduce((map, item) => { map.set(item, (map.get(item) || 0) + 1)), new Map())、両方のアレイについて、それらをループして、量が同じであることを確認しますか?多くの反復のように見えますが、より徹底的になります。
redbmk

制御配列からの除外を使用することもできます(見つかったときに要素を削除し、最後に長さが0であることを確認します)が、通常の場合は努力する価値がありません。
ライフコーダー

10

標準のjestからexpect.arrayContaining(array)を使用できます。

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

完璧な解決策!これは、選択した答えをする必要があります...
rohit_wason

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

ここでの考え方は、最初に2つの配列の長さが同じであるかどうかを判断し、次にすべての要素が他方の配列にあるかどうかを確認することです。


これは、アイテムの頻度を考慮していません:equal([1, 1, 2], [1, 2, 2])returns 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 2018

1
これらの配列では失敗します:[1,1,2,3]、[3,3,1,2]。
Torben Kohlmeier 2018年

1
@TorbenKohlmeierありがとう、私は私の答えを更新しました(非一意の配列に関して敗北を認めます)
Domi 2018年

0

現在、このユースケースにはマッチャーがあります。

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

しかし、それは一部ですjest-extended。つまり、Jestのコア機能としては利用できません。
bluenote 1020

-1

次のようなものを使用できます。

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

インポートを忘れないでくださいjasmine。またはあなたにそれを追加します.eslintrc


-4

Jestには、expect.arrayContainingあなたが望むことを正確に実行するという関数があります。

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

それらが同じ長さであるかどうかも確認することをお勧めします。

期待される配列は、受信した配列のサブセットです

ドキュメントによると。

編集:ジャスミンタグに気づかなかったのは残念です、これはJestで機能する方法です

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.