2つの配列を比較したいのですが…理想的には効率的です。派手なものは何もありません。それらが同一であればtrue
、そうでなければfalse
です。当然のことながら、比較演算子は機能しないようです。
var a1 = [1,2,3];
var a2 = [1,2,3];
console.log(a1==a2); // Returns false
console.log(JSON.stringify(a1)==JSON.stringify(a2)); // Returns true
それぞれの配列をJSONエンコードすることはできますが、それぞれの値を繰り返す必要なしに単純に配列を比較するためのより速いまたは「より良い」方法はありますか?
配列を比較するには、それらをループしてすべての値を比較します。
// Warn if overriding existing method
if(Array.prototype.equals)
console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});
[1, 2, [3, 4]].equals([1, 2, [3, 2]]) === false;
[1, "2,3"].equals([1, 2, 3]) === false;
[1, 2, [3, 4]].equals([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].equals([1, 2, 1, 2]) === true;
「しかし、文字列を比較する方がはるかに高速です-ループなし...」と言うかもしれませんが、そこにループがあることに注意してください。配列を文字列に変換する最初の再帰ループと、2つの文字列を比較する2番目の再帰ループ。したがって、このメソッドはstringを使用するよりも高速です。
大量のデータは、オブジェクトではなく、常に配列に保存する必要があると思います。ただし、オブジェクトを使用する場合は、部分的に比較することもできます。
方法は次のとおりです:
上記のとおり、2つのオブジェクトインスタンスは、現時点で同じデータが含まれていても等しくなることはありません。
({a:1, foo:"bar", numberOfTheBeast: 666}) == ({a:1, foo:"bar", numberOfTheBeast: 666}) //false
これには理由があります。たとえば、 オブジェクト内のプライベート変数
ただし、オブジェクト構造を使用してデータを含めるだけの場合、比較は可能です。
Object.prototype.equals = function(object2) {
//For the first loop, we only check for types
for (propName in this) {
//Check for inherited methods and properties - like .equals itself
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
//Return false if the return value is different
if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
return false;
}
//Check instance type
else if (typeof this[propName] != typeof object2[propName]) {
//Different types => not equal
return false;
}
}
//Now a deeper check using other objects property names
for(propName in object2) {
//We must check instances anyway, there may be a property that only exists in object2
//I wonder, if remembering the checked values from the first loop would be faster or not
if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
return false;
}
else if (typeof this[propName] != typeof object2[propName]) {
return false;
}
//If the property is inherited, do not check any more (it must be equa if both objects inherit it)
if(!this.hasOwnProperty(propName))
continue;
//Now the detail check and recursion
//This returns the script back to the array comparing
/**REQUIRES Array.equals**/
if (this[propName] instanceof Array && object2[propName] instanceof Array) {
// recurse into the nested arrays
if (!this[propName].equals(object2[propName]))
return false;
}
else if (this[propName] instanceof Object && object2[propName] instanceof Object) {
// recurse into another objects
//console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
if (!this[propName].equals(object2[propName]))
return false;
}
//Normal value comparison for strings and numbers
else if(this[propName] != object2[propName]) {
return false;
}
}
//If everything passed, let's say YES
return true;
}
ただし、これはクラスインスタンスやその他のものではなく、JSONのようなデータの比較に役立つことを忘れないでください。複雑なオブジェクトを比較したい場合は、 この答えと超長関数 を見てください。Array.equals
でこれを機能させるには、元の関数を少し編集する必要があります。
...
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
/**REQUIRES OBJECT COMPARE**/
else if (this[i] instanceof Object && array[i] instanceof Object) {
// recurse into another objects
//console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
...
両方の関数の小さなテストツール を作成しました。
indexOf
およびcontains
のネストされた配列Samy Bencherifが準備しました ネストされた配列内の特定のオブジェクトを検索する場合に便利な関数は、こちらから入手できます。 https://jsfiddle.net/SamyBencherif/8352y6yw/
これはスカラー配列に対してのみ機能しますが(下記の注を参照)、短いです。
array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
Rr、ECMAScript 6/CoffeeScript/TypeScript with Arrow Functions:
array1.length === array2.length && array1.every((value, index) => value === array2[index])
(注:ここでの 'スカラー'は、===
を使用して直接比較できる値を意味します。したがって、数値、文字列、参照によるオブジェクト、参照による関数です。比較演算子についての詳細は、 MDN参照 を参照してください)。
_ update _
私がコメントから読んだものから、配列をソートして比較すると正確な結果が得られるかもしれません。
array1.length === array2.length && array1.sort().every(function(value, index) { return value === array2.sort()[index]});
例えば:
array1 = [2,3,1,4];
array2 = [1,2,3,4];
それから上記のコードはtrue
を与えるでしょう
UnderscoreとLodashでは、配列とオブジェクトの重いコーディングプロジェクトにUnderscoreライブラリを使用するのが好きです。
_.isEqual(array1, array2) // returns a boolean
_.isEqual(object1, object2) // returns a boolean
これはJSON stringifyを使用してそれを行うための最も簡単な方法であると思います、そしてそれはいくつかの状況で最善の解決策かもしれません:
JSON.stringify(a1) === JSON.stringify(a2);
これはオブジェクトa1
とa2
を文字列に変換して比較できるようにします。順序はほとんどの場合重要です、それはそれが上記の答えの1つに示されているソートアルゴリズムを使用してオブジェクトをソートできるからです。
オブジェクトを比較するのではなく、オブジェクトの文字列表現を比較していることに注意してください。それはあなたが望むものではないかもしれません。
「同一」とはどういう意味ですか。たとえば、以下の配列a
とb
は同一ですか(入れ子になった配列に注意してください)。
var a = ["foo", ["bar"]], b = ["foo", ["bar"]];
厳密な等価性を使用して各配列の対応する要素を順番に比較し、それ自体が配列である配列要素を再帰的に比較しない最適化された配列比較関数があります。つまり、arraysIdentical(a, b)
はfalse
を返します。一般的なケースではうまくいきますが、JSONベースのソリューションとjoin()
ベースのソリューションは次のことを行いません。
function arraysIdentical(a, b) {
var i = a.length;
if (i != b.length) return false;
while (i--) {
if (a[i] !== b[i]) return false;
}
return true;
};
「間違った」ソリューションとは対照的に、「正しい」(「正しい」)だけである場合、特定の実装が「The Right Way™」であると言うのは間違っていると思います。 Tomášのソリューションは、文字列ベースの配列比較よりも明らかに改善されていますが、それが客観的に「正しい」という意味ではありません。とにかくrightとは何ですか?最速ですか?最も柔軟ですか?理解するのが最も簡単ですか?デバッグするのが最も速いですか?最小限の操作を使用しますか?副作用はありますか?あらゆるソリューションの中で最高のものを提供できるソリューションはありません。
トマシュは、彼の解決策は速いと言うことができましたが、私はそれが不必要に複雑であるとも言います。ネストされているかどうかに関係なく、すべての配列で機能するオールインワンソリューションを目指しています。実際、入力として単なる配列以上のものを受け入れ、「有効な」答えを与えようとします。
私の答えは、問題に対するアプローチが異なります。まず、配列のステップ実行にのみ関係する汎用arrayCompare
プロシージャから始めます。そこから、arrayEqual
やarrayDeepEqual
などの他の基本的な比較関数を作成します
// arrayCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayCompare = f => ([x,...xs]) => ([y,...ys]) =>
x === undefined && y === undefined
? true
: Boolean (f (x) (y)) && arrayCompare (f) (xs) (ys)
私の意見では、最高の種類のコードはコメントすら必要とせず、これも例外ではありません。ここではほとんど何も起きていないので、ほとんど手間をかけずにこの手順の動作を理解できます。確かに、ES6構文の一部は今では異質に思えるかもしれませんが、それはES6が比較的新しいからです。
タイプが示すように、arrayCompare
は比較関数f
と、2つの入力配列xs
およびys
を取ります。ほとんどの場合、入力配列の各要素に対してf (x) (y)
を呼び出すだけです。ユーザー定義のfalse
がf
を返す場合、早期のfalse
を返します。&&
の短絡評価のおかげです。そのため、これは、コンパレータが反復を早期に停止し、不要な場合に入力配列の残りの部分をループするのを防ぐことができることを意味します。
次に、arrayCompare
関数を使用して、必要な他の関数を簡単に作成できます。基本的なarrayEqual
から始めましょう…
// equal :: a -> a -> Bool
const equal = x => y =>
x === y // notice: triple equal
// arrayEqual :: [a] -> [a] -> Bool
const arrayEqual =
arrayCompare (equal)
const xs = [1,2,3]
const ys = [1,2,3]
console.log (arrayEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2) && (3 === 3) //=> true
const zs = ['1','2','3']
console.log (arrayEqual (xs) (zs)) //=> false
// (1 === '1') //=> false
そのような単純な。 arrayEqual
は、arrayCompare
と、===
(厳密な等価性)を使用してa
とb
を比較する比較関数で定義できます。
equal
も独自の関数として定義していることに注意してください。これは、別のデータ型(配列)のコンテキストで1次コンパレーターを利用するための高階関数としてのarrayCompare
の役割を強調しています。
代わりに==
を使用して、arrayLooseEqual
を簡単に定義できます。 1
(数値)と'1'
(文字列)を比較すると、結果はtrue
になります…
// looseEqual :: a -> a -> Bool
const looseEqual = x => y =>
x == y // notice: double equal
// arrayLooseEqual :: [a] -> [a] -> Bool
const arrayLooseEqual =
arrayCompare (looseEqual)
const xs = [1,2,3]
const ys = ['1','2','3']
console.log (arrayLooseEqual (xs) (ys)) //=> true
// (1 == '1') && (2 == '2') && (3 == '3') //=> true
おそらく、これは浅い比較にすぎないことに気づいたでしょう。トマシュのソリューションは、暗黙の深い比較を行うため、「The Right Way™」です。
arrayCompare
プロシージャは、深い同等性テストを簡単にする方法で使用するのに十分な汎用性があります…
// isArray :: a -> Bool
const isArray =
Array.isArray
// arrayDeepCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayDeepCompare = f =>
arrayCompare (a => b =>
isArray (a) && isArray (b)
? arrayDeepCompare (f) (a) (b)
: f (a) (b))
const xs = [1,[2,[3]]]
const ys = [1,[2,['3']]]
console.log (arrayDeepCompare (equal) (xs) (ys)) //=> false
// (1 === 1) && (2 === 2) && (3 === '3') //=> false
console.log (arrayDeepCompare (looseEqual) (xs) (ys)) //=> true
// (1 == 1) && (2 == 2) && (3 == '3') //=> true
そのような単純な。 another高階関数を使用してディープコンパレーターを構築します。今回は、arrayCompare
とa
が配列であるかどうかを確認するカスタムコンパレータを使用して、b
をラップしています。その場合は、arrayDeepCompare
を再適用します。それ以外の場合は、a
とb
をユーザー指定のコンパレーター(f
)と比較します。これにより、個々の要素を実際に比較する方法とは別に、深い比較動作を維持できます。つまり、上記の例が示すように、equal
、looseEqual
、または他のコンパレーターを使用してディープ比較できます。
arrayDeepCompare
はカリー化されているため、前の例でも行ったように部分的に適用できます
// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
arrayDeepCompare (equal)
// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
arrayDeepCompare (looseEqual)
私にとって、これはすでにトマシュのソリューションに対する明確な改善です。なぜなら、必要に応じてアレイの浅い比較または深い比較を明示的に選択できるからです。
オブジェクトの配列などがある場合はどうでしょうか?各オブジェクトが同じid
値を持っている場合、これらの配列を「等しい」と見なしたい場合があります…
// idEqual :: {id: Number} -> {id: Number} -> Bool
const idEqual = x => y =>
x.id !== undefined && x.id === y.id
// arrayIdEqual :: [a] -> [a] -> Bool
const arrayIdEqual =
arrayCompare (idEqual)
const xs = [{id:1}, {id:2}]
const ys = [{id:1}, {id:2}]
console.log (arrayIdEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2) //=> true
const zs = [{id:1}, {id:6}]
console.log (arrayIdEqual (xs) (zs)) //=> false
// (1 === 1) && (2 === 6) //=> false
そのような単純な。ここでは、Vanilla JSオブジェクトを使用しましたが、このタイプのコンパレーターはanyオブジェクトタイプに使用できます。カスタムオブジェクトも。この種の同等性テストをサポートするには、トマシュのソリューションを完全に作り直す必要があります。
オブジェクトを含む深い配列?問題ない。汎用性の高い汎用関数を作成したため、さまざまなユースケースで機能します。
const xs = [{id:1}, [{id:2}]]
const ys = [{id:1}, [{id:2}]]
console.log (arrayCompare (idEqual) (xs) (ys)) //=> false
console.log (arrayDeepCompare (idEqual) (xs) (ys)) //=> true
または、他の種類の完全に任意の比較を行いたい場合はどうでしょうか?たぶん、各x
が各y
よりも大きいかどうか知りたいのですが…
// gt :: Number -> Number -> Bool
const gt = x => y =>
x > y
// arrayGt :: [a] -> [a] -> Bool
const arrayGt = arrayCompare (gt)
const xs = [5,10,20]
const ys = [2,4,8]
console.log (arrayGt (xs) (ys)) //=> true
// (5 > 2) && (10 > 4) && (20 > 8) //=> true
const zs = [6,12,24]
console.log (arrayGt (xs) (zs)) //=> false
// (5 > 6) //=> false
実際、より少ないコードでより多くのことを行っていることがわかります。 arrayCompare
自体について複雑なことはなく、私たちが作成した各カスタムコンパレーターは非常に単純な実装を持っています。
簡単に、2つの配列の比較方法を正確に定義できます。浅い、深い、厳密な、ゆるい、オブジェクトプロパティ、任意の計算、またはこれらの任意の組み合わせ— すべて1つのプロシージャを使用 =、arrayCompare
。多分RegExp
コンパレーターを思い浮かべてください!私は子供たちがそれらの正規表現を愛する方法を知っています...
最速ですか?いや。しかし、おそらくどちらかである必要はありません。コードの品質を測定するために使用される唯一のメトリックが速度である場合、非常に優れたコードの多くが捨てられます。そのため、このアプローチを呼び出していますThe Practical Way。または、もっと公平にするために、APractical Way。この説明はこの答えに適しています。この答えは、他の答えと比較して実用的だとは言っていないからです。客観的に真実です。推論するのが非常に簡単な非常に少ないコードで高度な実用性を達成しました。他のコードでは、この説明を取得していないと言うことはできません。
それはあなたにとって「正しい」ソリューションになりますか?決定するのはyouです。そして、あなたのために他の誰もそれを行うことはできません。あなただけがあなたのニーズを知っています。ほとんどすべての場合、私は賢明で高速な種類よりも簡単で実用的で汎用性の高いコードを重視しています。あなたが評価するものは異なるかもしれませんので、あなたに合ったものを選んでください。
私の古い答えは、arrayEqual
を小さなプロシージャに分解することに焦点を当てていました。これは興味深い演習ですが、実際にはこの問題に取り組むための最良の(最も実用的な)方法ではありません。興味のある方は、この改訂履歴をご覧ください。
元の質問の精神では:
2つの配列を比較したいのですが、理想的には 効率的に です。 なし fancy 、同一であれば真、そうでなければ偽。
私はここで提案されているより簡単な提案のいくつかでパフォーマンステストを実行してきました results (fast to slow):
while (67%) Tim Down著)
var i = a1.length;
while (i--) {
if (a1[i] !== a2[i]) return false;
}
return true
every (69%) userによる2782196
a1.every((v,i)=> v === a2[i]);
reduce (74%) DEIによる)
a1.reduce((a, b) => a && a2.includes(b), true);
join & toString (78%) Gaizka Allendeによる&vivek
a1.join('') === a2.join('');
a1.toString() === a2.toString();
half toString (90%) Victor Palomoさんによる)
a1 == a2.toString();
stringify (100%) radtekによる)
JSON.stringify(a1) === JSON.stringify(a2);
注 以下の例では、配列がソートされた1次元配列であると仮定しています。
.length
の比較は一般的なベンチマークでは削除されました(いずれかの提案にa1.length === a2.length
を追加すると、パフォーマンスが最大10%向上します)。それぞれのスピードと限界を知っているあなたにとって最適な解決策を選んでください。無関係なメモ:この質問に対する完全に合法的な答えを得るために、下の投票ボタンでトリガーに恵まれたJohn Waynesの人々すべてに出会うのを見るのは興味深いです。
TomášZatoの答えを踏まえて、配列を繰り返し処理するのが最速であることに同意します。さらに(他の人がすでに述べたように)、この関数はcompareではなくequals/equalと呼ばれるべきです。これを踏まえて、私は類似性のために配列を比較することを扱うために関数を修正しました - すなわち、それらは同じ要素を持っていますが、順序が乱れています - .
Array.prototype.equals = function (array, strict) {
if (!array)
return false;
if (arguments.length == 1)
strict = true;
if (this.length != array.length)
return false;
for (var i = 0; i < this.length; i++) {
if (this[i] instanceof Array && array[i] instanceof Array) {
if (!this[i].equals(array[i], strict))
return false;
}
else if (strict && this[i] != array[i]) {
return false;
}
else if (!strict) {
return this.sort().equals(array.sort(), true);
}
}
return true;
}
この関数は、デフォルトでtrueになる追加のstrictパラメータを取ります。この厳密なパラメータは、内容とそれらの内容の順序の両方で配列を完全に等しくする必要があるか、単に同じ内容を含むだけでよいかを定義します。
例:
var arr1 = [1, 2, 3, 4];
var arr2 = [2, 1, 4, 3]; // Loosely equal to 1
var arr3 = [2, 2, 3, 4]; // Not equal to 1
var arr4 = [1, 2, 3, 4]; // Strictly equal to 1
arr1.equals(arr2); // false
arr1.equals(arr2, false); // true
arr1.equals(arr3); // false
arr1.equals(arr3, false); // false
arr1.equals(arr4); // true
arr1.equals(arr4, false); // true
また、この関数とこの例を使って簡単なjsfiddleを書きました。
http://jsfiddle.net/Roundaround/DLkxX/
これにはたくさんの答えがありますが、私は助けになると信じています。
const newArray = [ ...new Set( [...arr1, ...arr2] ) ]
配列の構造がどのように見えるかについては、質問では述べられていません。したがって、あなたが配列の中にネストされた配列もオブジェクトも含まないことを確実に知っているなら)私はこの答えに来ました)上記のコードは動作します。
何が起こるかというと、両方の配列を連結するために拡散演算子(...)を使用し、次にSetを使用して重複を排除します。 3つの配列すべてが同じサイズであれば、サイズを比較できます。
この答えも私が言ったように、要素の順序を無視する、正確な状況が私に起こったので、多分同じ状況にいる誰かがここで終わるかもしれない(私がしたように).
編集1.
Dmitry Grinkoの質問に答える: "なぜここでは、スプレッド演算子(...)を使ったのですか - ... new Set?それはうまくいきません"
このコードを見てください:
const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ new Set( [...arr1, ...arr2] ) ]
console.log(newArray)
あなたは得るでしょう
[ Set { 'a', 'b', 'c' } ]
その値で作業するには、いくつかのSetプロパティを使用する必要があります( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set を参照)。一方、このコードを使用すると、
const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ ...new Set( [...arr1, ...arr2] ) ]
console.log(newArray)
あなたは得るでしょう
[ 'a', 'b', 'c' ]
それが違いです。前者は私にセットを与えます、それは私がそのセットのサイズを得ることができるようにも働きますが、後者は私に必要な配列を与えます。
JSON.encodeと同じ行にjoin()を使用します。
function checkArrays( arrA, arrB ){
//check if lengths are different
if(arrA.length !== arrB.length) return false;
//slice so we do not effect the original
//sort makes sure they are in order
//join makes it a string so we can do a string compare
var cA = arrA.slice().sort().join(",");
var cB = arrB.slice().sort().join(",");
return cA===cB;
}
var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"]; //will return true
console.log( checkArrays(a,b) ); //true
console.log( checkArrays(a,c) ); //false
console.log( checkArrays(a,d) ); //false
console.log( checkArrays(a,e) ); //true
最後の比較がテストする型を気にしているのであれば、問題はあります。
function checkArrays( arrA, arrB ){
//check if lengths are different
if(arrA.length !== arrB.length) return false;
//slice so we do not effect the orginal
//sort makes sure they are in order
var cA = arrA.slice().sort();
var cB = arrB.slice().sort();
for(var i=0;i<cA.length;i++){
if(cA[i]!==cB[i]) return false;
}
return true;
}
var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];
console.log( checkArrays(a,b) ); //true
console.log( checkArrays(a,c) ); //false
console.log( checkArrays(a,d) ); //false
console.log( checkArrays(a,e) ); //false
順番がループであるよりも同じままである場合、ソートは不要です。
function checkArrays( arrA, arrB ){
//check if lengths are different
if(arrA.length !== arrB.length) return false;
for(var i=0;i<arrA.length;i++){
if(arrA[i]!==arrB[i]) return false;
}
return true;
}
var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];
console.log( checkArrays(a,a) ); //true
console.log( checkArrays(a,b) ); //false
console.log( checkArrays(a,c) ); //false
console.log( checkArrays(a,d) ); //false
console.log( checkArrays(a,e) ); //false
もしそれらが2つの数の配列または文字列だけであれば、これは簡単な1行のものです。
const array1 = [1, 2, 3];
const array2 = [1, 3, 4];
console.log(array1.join(',') === array2.join(',')) //false
const array3 = [1, 2, 3];
const array4 = [1, 2, 3];
console.log(array3.join(',') === array4.join(',')) //true
一次元配列の場合は、単に使用することができます:
arr1.sort().toString() == arr2.sort().toString()
これはまた、インデックスが一致しない配列の面倒を見るでしょう。
これがTypeScriptのバージョンです。
//https://stackoverflow.com/a/16436975/2589276
export function arraysEqual<T>(a: Array<T>, b: Array<T>): boolean {
if (a === b) return true
if (a == null || b == null) return false
if (a.length != b.length) return false
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false
}
return true
}
//https://stackoverflow.com/a/16436975/2589276
export function arraysDeepEqual<T>(a: Array<T>, b: Array<T>): boolean {
return JSON.stringify(a) === JSON.stringify(b)
}
モカのためのいくつかのテストケース:
it('arraysEqual', function () {
let a = [1,2]
let b = [1,2]
let c = [2,3]
let d = [2, 3]
let e = ['car','Apple','banana']
let f = ['car','Apple','banana']
let g = ['car','Apple','banan8']
expect(arraysEqual(a, b)).to.equal(true)
expect(arraysEqual(c, d)).to.equal(true)
expect(arraysEqual(a, d)).to.equal(false)
expect(arraysEqual(e, f)).to.equal(true)
expect(arraysEqual(f, g)).to.equal(false)
})
it('arraysDeepEqual', function () {
let a = [1,2]
let b = [1,2]
let c = [2,3]
let d = [2, 3]
let e = ['car','Apple','banana']
let f = ['car','Apple','banana']
let g = ['car','Apple','banan8']
let h = [[1,2],'Apple','banan8']
let i = [[1,2],'Apple','banan8']
let j = [[1,3],'Apple','banan8']
expect(arraysDeepEqual(a, b)).to.equal(true)
expect(arraysDeepEqual(c, d)).to.equal(true)
expect(arraysDeepEqual(a, d)).to.equal(false)
expect(arraysDeepEqual(e, f)).to.equal(true)
expect(arraysDeepEqual(f, g)).to.equal(false)
expect(arraysDeepEqual(h, i)).to.equal(true)
expect(arraysDeepEqual(h, j)).to.equal(false)
})
これは2つのソートされていない配列を比較します。
function areEqual(a, b) {
if ( a.length != b.length) {
return false;
}
return a.filter(function(i) {
return !b.includes(i);
}).length === 0;
}
これが私の解決策です:
/**
* Tests two data structures for equality
* @param {object} x
* @param {object} y
* @returns {boolean}
*/
var equal = function(x, y) {
if (typeof x !== typeof y) return false;
if (x instanceof Array && y instanceof Array && x.length !== y.length) return false;
if (typeof x === 'object') {
for (var p in x) if (x.hasOwnProperty(p)) {
if (typeof x[p] === 'function' && typeof y[p] === 'function') continue;
if (x[p] instanceof Array && y[p] instanceof Array && x[p].length !== y[p].length) return false;
if (typeof x[p] !== typeof y[p]) return false;
if (typeof x[p] === 'object' && typeof y[p] === 'object') { if (!equal(x[p], y[p])) return false; } else
if (x[p] !== y[p]) return false;
}
} else return x === y;
return true;
};
入れ子になったデータ構造を処理し、明らかにオブジェクトのメソッドを無視します。このメソッドを使ってObject.prototypeを拡張することさえ考えないでください。一度試したときにjQueryが壊れました。
ほとんどの配列では、ほとんどのシリアル化ソリューションよりもまだ高速です。これはおそらくオブジェクトレコードの配列に対する最速のcompareメソッドです。
every
( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/every )を使用して、これを機能的な方法で実行できます。
function compareArrays(array1, array2) {
if (array1.length === array2.length)
return array1.every((a, index) => a === array2[index])
else
return false
}
// test
var a1 = [1,2,3];
var a2 = [1,2,3];
var a3 = ['a', 'r', 'r', 'a', 'y', '1']
var a4 = ['a', 'r', 'r', 'a', 'y', '2']
console.log(compareArrays(a1,a2)) // true
console.log(compareArrays(a1,a3)) // false
console.log(compareArrays(a3,a4)) // false
var a1 = [1,2,3,6];
var a2 = [1,2,3,5];
function check(a, b) {
return (a.length != b.length) ? false :
a.every(function(row, index) {
return a[index] == b[index];
});
}
check(a1, a2);
////// _ OR ///////
var a1 = [1,2,3,6];
var a2 = [1,2,3,6];
function check(a, b) {
return (a.length != b.length) ? false :
!(a.some(function(row, index) {
return a[index] != b[index];
}));
}
check(a1, a2)
私は平易なJS
とECMAScript 2015
を信じています。これは甘くて理解しやすいものです。
var is_arrays_compare_similar = function (array1, array2) {
let flag = true;
if (array1.length == array2.length) {
// check first array1 object is available in array2 index
array1.every( array_obj => {
if (flag) {
if (!array2.includes(array_obj)) {
flag = false;
}
}
});
// then vice versa check array2 object is available in array1 index
array2.every( array_obj => {
if (flag) {
if (!array1.includes(array_obj)) {
flag = false;
}
}
});
return flag;
} else {
return false;
}
}
それが誰かを助けることを願っています。
2つの配列を比較する:
var arr1 = [1,2,3];
var arr2 = [1,2,3];
function compare(arr1,arr2)
{
if((arr1 == arr2) && (arr1.length == arr2.length))
return true;
else
return false;
}
呼び出し機能
var isBool = compare(arr1.sort().join(),arr2.sort().join());
[a]からそれぞれ選択し、[b]:からすべてループします。結果:1、5
var a = [1,4,5,9];
var b = [1,6,7,5];
for (i = 0; i < a.length; i++) {
for (z = 0; z < a.length; z++) {
if (a[i] === b[z]) {
console.log(b[z]); // if match > console.log it
}
}
}
その理由は、アイデンティティまたは厳密な演算子(===)が型変換なしと比較されるためです。つまり、両方の値が同じ値と同じ型を持たない場合、それらは等しいと見なされません。
このリンクを見てください、それはあなたを疑いからあなたを連れて行きます アイデンティティ演算子がどのように機能するかを理解する簡単な方法
このスクリプトは、Object、Arrays、および多次元配列を比較します。
function compare(a,b){
var primitive=['string','number','boolean'];
if(primitive.indexOf(typeof a)!==-1 && primitive.indexOf(typeof a)===primitive.indexOf(typeof b))return a===b;
if(typeof a!==typeof b || a.length!==b.length)return false;
for(i in a){
if(!compare(a[i],b[i]))return false;
}
return true;
}
最初の行は、それがプリミティブ型かどうかを調べます。もしそうなら、それは2つのパラメータを比較します。
それらがオブジェクトであれば。それはObjectを繰り返し処理し、すべての要素を再帰的にチェックします。
使用法:
var a=[1,2,[1,2]];
var b=[1,2,[1,2]];
var isEqual=compare(a,b); //true
この関数は、任意の形状と二次元性の2つの配列を比較します。
function equals(a1, a2) {
if (!Array.isArray(a1) || !Array.isArray(a2)) {
throw new Error("Arguments to function equals(a1, a2) must be arrays.");
}
if (a1.length !== a2.length) {
return false;
}
for (var i=0; i<a1.length; i++) {
if (Array.isArray(a1[i]) && Array.isArray(a2[i])) {
if (equals(a1[i], a2[i])) {
continue;
} else {
return false;
}
} else {
if (a1[i] !== a2[i]) {
return false;
}
}
}
return true;
}
両方の配列が同じ要素を持っているが同じ順序ではない場合、コードはケースを適切に処理しません。
要素が数値である2つの配列を比較する例で私のコードを見てください。他の要素タイプに合わせて変更または拡張できます(.toString()の代わりに.join()を使用します)。
var a1 = [1,2,3];
var a2 = [1,2,3];
const arraysAreEqual = a1.sort().toString()==a2.sort().toString();
// true if both arrays have same elements else false
console.log(arraysAreEqual);
私の場合、比較された配列は数字と文字列のみを含みます。この関数は、配列が同じ要素を含んでいるかどうかを示します。
function are_arrs_match(arr1, arr2){
return arr1.sort().toString() === arr2.sort().toString()
}
テストしましょう。
arr1 = [1, 2, 3, 'nik']
arr2 = ['nik', 3, 1, 2]
arr3 = [1, 2, 5]
console.log (are_arrs_match(arr1, arr2)) //true
console.log (are_arrs_match(arr1, arr3)) //false
TomášZatoのアイデアを拡張する。 TomasのArray.prototype.compareは、実際はArray.prototype.compareIdenticalという名前にする必要があります。
それは渡します:
[1, 2, [3, 4]].compareIdentical ([1, 2, [3, 2]]) === false;
[1, "2,3"].compareIdentical ([1, 2, 3]) === false;
[1, 2, [3, 4]].compareIdentical ([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].compareIdentical ([1, 2, 1, 2]) === true;
しかし失敗します:
[[1, 2, [3, 2]],1, 2, [3, 2]].compareIdentical([1, 2, [3, 2],[1, 2, [3, 2]]])
これが(私の考えでは)より良いバージョンです。
Array.prototype.compare = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
this.sort();
array.sort();
for (var i = 0; i < this.length; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].compare(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
非常に少ないコードでの別のアプローチ( Array reduce および /Arrayを含む を使用):
arr1.length == arr2.length && arr1.reduce((a, b) => a && arr2.includes(b), true)
次数の等号も比較したい場合は、
arr1.length == arr2.length && arr1.reduce((a, b, i) => a && arr2[i], true)
length
チェックは、一方の配列内の要素のセットが他方の配列のサブセットではないことを確認します。
リデューサーは、1つの配列を調べて他の配列内の各項目を検索するために使用されます。一つの項目が見つからない場合、reduce関数はfalse
を返します。
JSON.stringify(collectionNames).includes(JSON.stringify(sourceNames)) ? array.Push(collection[i]) : null
これが私のやり方です。
順序を比較するかどうかを選択するオプションがあります。
function arraysEqual(a1, a2, compareOrder) {
if (a1.length !== a2.length) {
return false;
}
return a1.every(function(value, index) {
if (compareOrder) {
return value === a2[index];
} else {
return a2.indexOf(value) > -1;
}
});
}
再帰的 &は _ネスト_ 配列で動作します。
function ArrEQ(a1,a2){
return(
//:Are both elements arrays?
Array.isArray(a1)&&Array.isArray(a2)
?
//:Yes: Test each entry for equality:
a1.every((v,i)=>(ArrEQ(v,a2[i])))
:
//:No: Simple Comparison:
(a1===a2)
);;
};;
console.log( "Works With Nested Arrays:" );
console.log( ArrEQ(
[1,2,3,[4,5,[6,"SAME/IDENTICAL"]]],
[1,2,3,[4,5,[6,"SAME/IDENTICAL"]]]
));;
console.log( ArrEQ(
[1,2,3,[4,5,[6,"DIFFERENT:APPLES" ]]],
[1,2,3,[4,5,[6,"DIFFERENT:ORANGES"]]]
));;
In a simple way uning stringify but at same time thinking in complex arrays:
**Simple arrays**:
var a = [1,2,3,4];
var b = [4,2,1,4];
JSON.stringify(a.sort()) === JSON.stringify(b.sort()) // true
**Complex arrays**:
var a = [{id:5,name:'as'},{id:2,name:'bes'}];
var b = [{id:2,name:'bes'},{id:5,name:'as'}];
JSON.stringify(a.sort(function(a,b) {return a.id - b.id})) === JSON.stringify(b.sort(function(a,b) {return a.id - b.id})) // true
**Or we can create a sort function**
function sortX(a,b) {
return a.id -b.id; //change for the necessary rules
}
JSON.stringify(a.sort(sortX)) === JSON.stringify(b.sort(sortX)) // true
簡単な方法:
function equals(a, b) {
if ((a && !b) || (!a && b) || (!a && !b) || (a.length !== b.length)) {
return false;
}
var isDifferent = a.some(function (element, index) {
return element !== b[index];
});
return !isDifferent;
}
私の解決策は、配列ではなくオブジェクトを比較します。これは、配列がオブジェクトであるため、Tomášと同じように機能しますが、警告はありません。
Object.prototype.compare_to = function(comparable){
// Is the value being compared an object
if(comparable instanceof Object){
// Count the amount of properties in @comparable
var count_of_comparable = 0;
for(p in comparable) count_of_comparable++;
// Loop through all the properties in @this
for(property in this){
// Decrements once for every property in @this
count_of_comparable--;
// Prevents an infinite loop
if(property != "compare_to"){
// Is the property in @comparable
if(property in comparable){
// Is the property also an Object
if(this[property] instanceof Object){
// Compare the properties if yes
if(!(this[property].compare_to(comparable[property]))){
// Return false if the Object properties don't match
return false;
}
// Are the values unequal
} else if(this[property] !== comparable[property]){
// Return false if they are unequal
return false;
}
} else {
// Return false if the property is not in the object being compared
return false;
}
}
}
} else {
// Return false if the value is anything other than an object
return false;
}
// Return true if their are as many properties in the comparable object as @this
return count_of_comparable == 0;
}
これがあなたや他の誰かが答えを探すのに役立つことを願っています。
数値/文字列/配列/オブジェクトを扱う再帰的なcmp関数
<script>
var cmp = function(element, target){
if(typeof element !== typeof target)
{
return false;
}
else if(typeof element === "object" && (!target || !element))
{
return target === element;
}
else if(typeof element === "object")
{
var keys_element = Object.keys(element);
var keys_target = Object.keys(target);
if(keys_element.length !== keys_target.length)
{
return false;
}
else
{
for(var i = 0; i < keys_element.length; i++)
{
if(keys_element[i] !== keys_target[i])
return false;
if(!cmp(element[keys_element[i]], target[keys_target[i]]))
return false;
}
return true;
}
}
else
{
return element === target;
}
};
console.log(cmp({
key1: 3,
key2: "string",
key3: [4, "45", {key4: [5, "6", false, null, {v:1}]}]
}, {
key1: 3,
key2: "string",
key3: [4, "45", {key4: [5, "6", false, null, {v:1}]}]
})); // true
console.log(cmp({
key1: 3,
key2: "string",
key3: [4, "45", {key4: [5, "6", false, null, {v:1}]}]
}, {
key1: 3,
key2: "string",
key3: [4, "45", {key4: [5, "6", undefined, null, {v:1}]}]
})); // false
</script>
試した deep-equal それは働いた
var eq = require('deep-equal');
eq({a: 1, b: 2, c: [3, 4]}, {c: [3, 4], a: 1, b: 2});
function compareArrays(arrayA, arrayB) {
if (arrayA.length != arrayB.length) return true;
for (i = 0; i < arrayA.length; i++)
if (arrayB.indexOf(arrayA[i]) == -1) {
return true;
}
}
for (i = 0; i < arrayB.length; i++) {
if (arrayA.indexOf(arrayB[i]) == -1) {
return true;
}
}
return false;
}
_複数の_ 引数を _ネスト_ /配列と組み合わせて使用します。
//:Return true if all of the arrays equal.
//:Works with nested arrays.
function AllArrEQ(...arrays){
for(var i = 0; i < (arrays.length-1); i++ ){
var a1 = arrays[i+0];
var a2 = arrays[i+1];
var res =(
//:Are both elements arrays?
Array.isArray(a1)&&Array.isArray(a2)
?
//:Yes: Compare Each Sub-Array:
//:v==a1[i]
a1.every((v,i)=>(AllArrEQ(v,a2[i])))
:
//:No: Simple Comparison:
(a1===a2)
);;
if(!res){return false;}
};;
return( true );
};;
console.log( AllArrEQ(
[1,2,3,[4,5,[6,"ALL_EQUAL" ]]],
[1,2,3,[4,5,[6,"ALL_EQUAL" ]]],
[1,2,3,[4,5,[6,"ALL_EQUAL" ]]],
[1,2,3,[4,5,[6,"ALL_EQUAL" ]]],
));;
この例よりも複雑なシナリオでは、配列を結合し、比較する文字列を作成するために使用しました。
var a1 = [1,2,3];
var a2 = [1,2,3];
if (a1.length !== a2.length) {
console.log('a1 and a2 are not equal')
}else if(a1.join(':') === a2.join(':')){
console.log('a1 and a2 are equal')
}else{
console.log('a1 and a2 are not equal')
}
この質問に対するトップの答えは正しいものですが、提供されているコードは多少の改良を加えることができます。
以下は配列とオブジェクトを比較するための私自身のコードです。コードは短くて簡単です:
Array.prototype.equals = function(otherArray) {
if (!otherArray || this.length != otherArray.length) return false;
return this.reduce(function(equal, item, index) {
var otherItem = otherArray[index];
var itemType = typeof item, otherItemType = typeof otherItem;
if (itemType !== otherItemType) return false;
return equal && (itemType === "object" ? item.equals(otherItem) : item === otherItem);
}, true);
};
if(!Object.prototype.keys) {
Object.prototype.keys = function() {
var a = [];
for (var key in this) {
if (this.hasOwnProperty(key)) a.Push(key);
}
return a;
}
Object.defineProperty(Object.prototype, "keys", {enumerable: false});
}
Object.prototype.equals = function(otherObject) {
if (!otherObject) return false;
var object = this, objectKeys = object.keys();
if (!objectKeys.equals(otherObject.keys())) return false;
return objectKeys.reduce(function(equal, key) {
var value = object[key], otherValue = otherObject[key];
var valueType = typeof value, otherValueType = typeof otherValue;
if (valueType !== otherValueType) return false;
// this will call Array.prototype.equals for arrays and Object.prototype.equals for objects
return equal && (valueType === "object" ? value.equals(otherValue) : value === otherValue);
}, true);
}
Object.defineProperty(Object.prototype, "equals", {enumerable: false});
このコードは、オブジェクトにネストされた配列と配列にネストされたオブジェクトをサポートします。
あなたは完全な一連のテストを見て、この返信であなた自身でコードをテストすることができます: https://repl.it/Esfz/3
これが好きな人のためのCoffeeScript版です。
Array.prototype.equals = (array) ->
return false if not array # if the other array is a falsy value, return
return false if @length isnt array.length # compare lengths - can save a lot of time
for item, index in @
if item instanceof Array and array[index] instanceof Array # Check if we have nested arrays
if not item.equals(array[index]) # recurse into the nested arrays
return false
else if this[index] != array[index]
return false # Warning - two different object instances will never be equal: {x:20} != {x:20}
true
すべてのクレジットは@ tomas-zatoに行きます。
実のところ、 Lodashdocumentation では、相違点と類似点の両方について新しい配列を比較して返すための2つの非常に良い例を示しています(それぞれ以下の例で)。
import { differenceWith, intersectionWith, isEqual } from 'lodash'
differenceWith(
[{ a: 1 }, { b: 1 }],
[{ a: 1 }, { b: 1 }, { c: 1 }],
isEqual
) // []... ????the bigger array needs to go first!
differenceWith(
[{ a: 1 }, { b: 1 }, { c: 1 }],
[{ a: 1 }, { b: 1 }],
isEqual,
) // [{ c: 1 }] ????
intersectionWith(
[{ a: 1 }, { b: 1 }],
[{ a: 1 }, { b: 1 }, { c: 1 }],
isEqual,
) // [{ a: 1 }, { b: 1 }] ????this one doesn't care about which is bigger
どの配列が大きくなるかが必ずしもわからない場合は、次のようにしてヘルパー関数を作成できます。
const biggerFirst = (arr1, arr2) => {
return arr1.length > arr2.length ? [arr1, arr2] : [arr2, arr1]
}
const [big, small] = biggerFirst(
[{ a: 1 }, { b: 1 }],
[{ a: 1 }, { b: 1 }, { c: 1 }],
)
differenceWith(big, small, isEqual) // ????even though we have no idea which is bigger when they are fed to biggerFirst()
私が言うことができることから、これらは同様に深く一致します、それでそれはかなり素晴らしいです。
私はすべてにライブラリを当てにすることを称賛すべきではないと思いますが、これは私が本当によくある問題に対して私が見つけた最も簡潔でクリーンな解決策です。誰かに役立つことを願っています!
これを行うには非常に短い方法です。
function arrEquals(arr1, arr2){
arr1.length == arr2.length &&
arr1.filter(elt=>arr2.includes(elt)).length == arr1.length
}
すでにいくつかの素晴らしい答えがあります。しかし、配列を比較する上で信頼できることが証明されている別のアイデアを共有したいと思います。 JSON.stringify() を使用して、2つの配列を比較できます。配列から文字列を作成し、2つの配列から得られた2つの文字列が等しいかどうか比較します。
JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:1},2]) //true
JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},2]) //false
JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},[3,4],2]) //false
JSON.stringify([1,{a:1},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //false
JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //true
JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false
JSON.stringify([1,{a:2},[3,4,[4]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false
JSON.stringify([1,{a:2},[3,4,[5]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //true
配列が単純で順序が重要な場合は、この2行が役立つことがあります。
//Assume
var a = ['a','b', 'c']; var b = ['a','e', 'c'];
if(a.length !== b.length) return false;
return !a.reduce(
function(prev,next,idx, arr){ return prev || next != b[idx] },false
);
'a'の少なくとも1つの要素が 'b'の要素と等しくない場合、Reduceは配列の1つを調べて 'false'を返します。