JavaScript で 2 つの配列が同じ内容かどうかを比較する場合、=== 演算子は使えません。配列はオブジェクト(参照型)のため、=== はメモリ上の同じオブジェクトかどうかを比較するだけです。この記事では、配列の内容を正しく比較する方法を目的別に解説します。
この記事でわかること
・=== で配列が比較できない理由
・every で要素ごとに比較する方法
・JSON.stringify による簡易比較とその限界
・順序を無視して同じ要素を持つかの比較
・ネスト配列の再帰的な比較
・オブジェクト配列の比較
・差分(片方にしかない要素)の取得
・共通要素(積集合)の取得
・=== で配列が比較できない理由
・every で要素ごとに比較する方法
・JSON.stringify による簡易比較とその限界
・順序を無視して同じ要素を持つかの比較
・ネスト配列の再帰的な比較
・オブジェクト配列の比較
・差分(片方にしかない要素)の取得
・共通要素(積集合)の取得
=== で配列が比較できない理由
JavaScript
const a = [1, 2, 3]; const b = [1, 2, 3]; console.log(a === b); // false(同じ内容でも参照が異なる) console.log(a == b); // false(== でも同じ) // 同じ参照なら true const c = a; console.log(a === c); // true(同じオブジェクトを指している)
=== は配列の参照(メモリアドレス)を比較します。内容が同じでも別のオブジェクトなら false です。配列の内容を比較するには、以下の方法を使います。
比較方法の早見表
| 方法 | 順序 | ネスト配列 | パフォーマンス | 推奨場面 |
|---|---|---|---|---|
| every + length | 順序一致 | × 非対応 | ○ 高速 | プリミティブ値の 1 次元配列 |
| JSON.stringify | 順序一致 | ○ 対応 | △ 中程度 | 手軽な比較(制限あり) |
| ソート + every | 順序無視 | × 非対応 | △ ソートコスト | 同じ要素を持つかの判定 |
| 再帰関数 | 順序一致 | ○ 対応 | △ 再帰コスト | ネスト配列・オブジェクト配列 |
every で要素ごとに比較する(推奨)
最もシンプルで高速な方法です。配列の長さを比較した後、every で各要素が一致するかを確認します。
JavaScript
function arraysEqual(a, b) {
if (a.length !== b.length) return false;
return a.every((val, i) => val === b[i]);
}
console.log(arraysEqual([1, 2, 3], [1, 2, 3])); // true
console.log(arraysEqual([1, 2, 3], [1, 2, 4])); // false
console.log(arraysEqual([1, 2], [1, 2, 3])); // false(長さが異なる)
every は不一致の要素が見つかった時点で即座に false を返すため、大きな配列でもパフォーマンスが良好です。この方法はプリミティブ値(数値・文字列・真偽値)の 1 次元配列にのみ有効です。ネスト配列やオブジェクト配列の比較には対応していません。
JSON.stringify で比較する(簡易版)
配列を JSON 文字列に変換して文字列比較する方法です。コードが簡潔で、ネスト配列にも対応しています。
JavaScript
function arraysEqualJSON(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
console.log(arraysEqualJSON([1, 2, 3], [1, 2, 3])); // true
console.log(arraysEqualJSON([1, [2, 3]], [1, [2, 3]])); // true(ネストも対応)
console.log(arraysEqualJSON([1, 2, 3], [3, 2, 1])); // false(順序が異なる)
JSON.stringify の制限
| ケース | 動作 | 問題 |
|---|---|---|
undefined |
null に変換される |
[1, undefined, 3] → [1, null, 3] |
| 関数 | 省略される | オブジェクト内の関数が消える |
NaN |
null に変換される |
[NaN] → [null] |
Date |
文字列に変換される | Date オブジェクトが文字列になる |
| 循環参照 | エラー | TypeError: Converting circular structure |
undefined・NaN・関数・Date を含む配列では JSON.stringify の比較が正しく動作しません。これらが含まれる可能性がある場合は every による比較か再帰関数を使ってください。
順序を無視して比較する
「同じ要素を持っているか」を順序に関係なく判定する方法です。
ソートしてから比較する
JavaScript
function arraysEqualUnordered(a, b) {
if (a.length !== b.length) return false;
const sortedA = [...a].sort();
const sortedB = [...b].sort();
return sortedA.every((val, i) => val === sortedB[i]);
}
console.log(arraysEqualUnordered([3, 1, 2], [1, 2, 3])); // true
console.log(arraysEqualUnordered([1, 2, 2], [2, 1, 2])); // true
console.log(arraysEqualUnordered([1, 2, 3], [1, 2, 4])); // false
重複を無視して「同じ値の集合か」を判定する
JavaScript
function setsEqual(a, b) {
const setA = new Set(a);
const setB = new Set(b);
if (setA.size !== setB.size) return false;
for (const val of setA) {
if (!setB.has(val)) return false;
}
return true;
}
console.log(setsEqual([1, 2, 3], [3, 2, 1])); // true
console.log(setsEqual([1, 2, 2], [1, 2])); // true(重複は無視)
console.log(setsEqual([1, 2, 3], [1, 2, 3, 4])); // false
重複の数まで一致させたい場合はソート版を、重複を無視して値の集合として比較したい場合は Set 版を使い分けてください。
ネスト配列を再帰的に比較する
JavaScript
function deepEqual(a, b) {
// 型が異なる or プリミティブ値の比較
if (a === b) return true;
if (typeof a !== typeof b) return false;
// 配列の比較
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false;
return a.every((val, i) => deepEqual(val, b[i]));
}
// オブジェクトの比較
if (typeof a === "object" && a !== null && b !== null) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every(key => deepEqual(a[key], b[key]));
}
return false;
}
// ネスト配列
console.log(deepEqual([1, [2, [3]]], [1, [2, [3]]])); // true
console.log(deepEqual([1, [2, [3]]], [1, [2, [4]]])); // false
// オブジェクト配列
console.log(deepEqual(
[{ id: 1, name: "A" }],
[{ id: 1, name: "A" }]
)); // true
deepEqual は配列・オブジェクト・プリミティブ値のすべてに対応する汎用的な比較関数です。ただし循環参照には対応していません。
差分と共通要素を取得する
差分(片方にしかない要素)
JavaScript
function difference(a, b) {
const setB = new Set(b);
return a.filter(x => !setB.has(x));
}
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];
console.log(difference(arr1, arr2)); // [1, 2](arr1 にあって arr2 にない)
console.log(difference(arr2, arr1)); // [6, 7](arr2 にあって arr1 にない)
対称差分(どちらか片方にしかない要素の合計)
JavaScript
function symmetricDifference(a, b) {
return [...difference(a, b), ...difference(b, a)];
}
console.log(symmetricDifference(arr1, arr2)); // [1, 2, 6, 7]
共通要素(積集合)
JavaScript
function intersection(a, b) {
const setB = new Set(b);
return a.filter(x => setB.has(x));
}
console.log(intersection(arr1, arr2)); // [3, 4, 5]
和集合(すべての要素を重複なしで結合)
JavaScript
function union(a, b) {
return [...new Set([...a, ...b])];
}
console.log(union(arr1, arr2)); // [1, 2, 3, 4, 5, 6, 7]
差分・共通・和集合の計算では
Set を使うと includes より高速(O(1) vs O(n))です。大きな配列では特にパフォーマンスの差が出ます。関連記事
- 配列の使い方まとめ — 作成・追加・削除・ループ・スプレッド構文
- includes の使い方 — 配列・文字列の存在チェック
- 配列の重複を削除する方法 — Set・filter
- 配列の map・filter・reduce の使い方
- some で配列の要素が条件に一致するか判定する方法
- slice の使い方 — 配列の切り出し
よくある質問
Q=== で配列を比較できないのはなぜですか?
A配列はオブジェクト(参照型)のため、
=== は同じオブジェクトかどうか(参照の一致)を比較します。内容が同じでも別々に作成した配列は異なるオブジェクトなので false になります。QJSON.stringify で比較して問題ないですか?
Aプリミティブ値のみの配列なら実用上問題ありません。ただし
undefined・NaN・関数・Date を含む場合は正しく比較できません。また、大きな配列では文字列変換のコストが高くなります。Q順序を無視して同じ要素を持つかを判定するには?
A
[...a].sort() と [...b].sort() でソートしてから every で比較します。重複も無視したい場合は Set に変換して size と has で比較してください。Qオブジェクト配列の比較はどうすればよいですか?
AJSON.stringify で簡易比較するか、再帰的な deepEqual 関数を自作します。lodash の
_.isEqual を使えば最も手軽にディープ比較ができます。Q2 つの配列の差分(片方にしかない要素)を取得するには?
A
a.filter(x => !new Set(b).has(x)) で a にあって b にない要素を取得できます。Set を使うと includes より高速です。まとめ
JavaScript で配列を比較する方法を目的別に整理しました。
- === は使えない: 参照比較のため、内容が同じでも別オブジェクトなら false
- every + length: プリミティブ値の 1 次元配列に最適(高速・推奨)
- JSON.stringify: 簡潔だが undefined・NaN・関数に制限あり
- 順序無視: ソート + every、または Set で集合比較
- ネスト配列: 再帰的な deepEqual 関数で対応
- 差分・共通・和集合: filter + Set で高速に計算
「何を比較したいのか」(順序込み / 順序無視 / 重複考慮 / ネスト対応)を明確にし、最適な方法を選んでください。