JavaScriptの配列型にはequalsメソッドが存在しません。また比較演算子による比較は、等価の判定に対応していません。
[1, 2] == [1, 2] // false
[1, 2].equals([1, 2]) // Uncaught TypeError: [1,2].equals is not a function at <anonymous>:1:8
配列同士の比較を行うためには、各要素を個別に比較したり、配列を文字列化した結果を比較することなどが有効です。
目次
- 繰り返し文による要素比較(確実な方法)
- JSON文字列による比較(簡潔な方法。比較的厳格な比較)
- toStringによる文字列比較(簡易な方法。若干抽象的で曖昧な比較)
繰り返し文による要素比較
for文で個別に要素を比較するのがもっとも確実です。比較の際には!==
演算子による厳格な比較が必要となります。!=
による曖昧な比較を行った場合には、1, "1"
や0, ""
、null, undefined
が等価な値として判定されてしまうため注意が必要です。
function array_equal(a, b) {
if (!Array.isArray(a)) return false;
if (!Array.isArray(b)) return false;
if (a.length != b.length) return false;
for (var i = 0, n = a.length; i < n; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
array_equal( [1, 2], [1, 2] ) // true
array_equal( [1, 2], [1, "2"] ) // false
array_equal( [0, 1], ["", "1"] ) // false
JSON文字列による比較
Array型の配列を安全かつ正確に比較するためには、JSON形式の文字列による比較も有効です。JSON.stringify
メソッドを用いて配列を文字列化し、両値を比較します。
JSON.stringify([1, 2]) == JSON.stringify([1, 2]) // true
JSON.stringify([1, 2]) == JSON.stringify([2, 1]) // false
JSON形式による文字列化には、型の種類に応じた厳格な変換が行われるというメリットがあります。またnull
やundefined
に対する変換もエラーにならず、安定的な値に変換されます。
JSON.stringify([1, "2"]) // "[1,"2"]"
JSON.stringify(null) // "null"(安全)
JSON.stringify([[1],{k:2}]) // "[[1],{"k":2}]"(入れ子構造やオブジェクト型要素にも対応)
JSON.stringify([{b:2,a:1}]) // "[{"b":2,"a":1}]"(順序を維持するため注意)
toStringによる文字列比較
toString
メソッドの結果を文字列比較する方法も知られていますが、値がnull
やundefined
の場合には実行時エラーが発生してしまいます。
var a = [1, 2], b = null;
a.toString() == a.toString() // true
a.toString() == b.toString() // Uncaught TypeError: Cannot read property 'toString' of null at <anonymous>:1:19
この問題に対処するためには、toStringの代わりに、文字列結合やStringコンストラクタによる文字列化の結果を活用する必要があります。
var a = [1, 2], b = null;
a + "" == a + "" // true
a + "" == b + "" // false
String(b) == String(b) // true("null"文字列同士の比較が行われるため)
なおオブジェクト型や配列型の値を含んだ配列の場合は、toString
では厳格な文字列化が行われず、厳密な比較が行えなくなるという問題があるため注意してください。# JSON文字列による比較が必要となります。
// 厳格なシリアライズが行われない
[{k:1}].toString() // "[object Object]"
// 入れ子状態の配列要素は同列に展開されてしまう
[[1, 2], [3]].toString() // "1,2,3"
[[1], [2, 3]].toString() // "1,2,3"
// JSON形式であればより厳格な文字列化が可能
JSON.stringify([[1, 2], [3]]) // "[[1,2],[3]]"
JSON.stringify([[1], [2, 3]]) // "[[1],[2,3]]"
もっともtoStringスタイルの比較は、異なるの型同士の等価比較が行えるという特徴があります。
// 1 == "1" 相当の等価判定
[1, "2"].toString() == ["1", 2].toString() // true
[1, "2"].toString() // "1,2"
["1", 2].toString() // "1,2"
// 1 === "1" 相当の等値判定
JSON.stringify([1, "2"]) == JSON.stringify(["1", 2]) // false
JSON.stringify([1, "2"]) // "[1,"2"]"
JSON.stringify(["1", 2]) // "["1",2]"
ちなみにtoStringスタイルによる比較では、いずれか一方を文字列化するだけでも等価判定が行えます。
[1, 2] == [1, 2].toString() // true
[1, 2] === [1, 2].toString() // false
((a, b) => a == b + "")([1, 2], [1, 2]) // true
配列の比較対象が文字列の場合には、自身の配列をtoStringメソッドで文字列に変換した上で、実際の比較を行うことになるためです。例えば[] == ""
では[].toString() == ""
相当の処理が行われることになります。
応用方法
同一の要素を含むかどうかを判定したい場合には要素のソートを行うとよいでしょう。
JSON.stringify([1, 2].sort()) == JSON.stringify([2, 1].sort()) // true
[1, "2"].sort().toString() == [2, "1"].sort().toString() // true
["a", "b"].sort() + "" == ["b", "a"].sort() + "" // true
一般的な方法ではありませんが、比較演算子を用いた等価判定のテクニックもあります。比較演算子は内部的にtoString
の結果を比較しています。
var a = [1, 2], b = [2, 1];
!(a < b || a > b) // false
!(a < a || a > a) // true
// オブジェクトを含んだ配列には非対応
!([{k: 1}] < [{k: 1}] || [{k: 1}] > [{k: 1}]) // true
!([{k: 1}] < [{k: 2}] || [{k: 1}] > [{k: 2}]) // true
~~~~
// a==b+""
// !(a<b||a>b)
// ((a,b)=>!(a<b||a>b))([1,2],[1,2])