【JavaScript】配列を比較する方法|=== が使えない理由・every・JSON.stringify・ネスト配列・差分と共通要素の取得まで解説

JavaScript で 2 つの配列が同じ内容かどうかを比較する場合、=== 演算子は使えません。配列はオブジェクト(参照型)のため、=== はメモリ上の同じオブジェクトかどうかを比較するだけです。この記事では、配列の内容を正しく比較する方法を目的別に解説します。

この記事でわかること
・=== で配列が比較できない理由
・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))です。大きな配列では特にパフォーマンスの差が出ます。

関連記事

よくある質問

Q=== で配列を比較できないのはなぜですか?
A配列はオブジェクト(参照型)のため、===同じオブジェクトかどうか(参照の一致)を比較します。内容が同じでも別々に作成した配列は異なるオブジェクトなので false になります。
QJSON.stringify で比較して問題ないですか?
Aプリミティブ値のみの配列なら実用上問題ありません。ただし undefinedNaN・関数・Date を含む場合は正しく比較できません。また、大きな配列では文字列変換のコストが高くなります。
Q順序を無視して同じ要素を持つかを判定するには?
A[...a].sort()[...b].sort() でソートしてから every で比較します。重複も無視したい場合は Set に変換して sizehas で比較してください。
Qオブジェクト配列の比較はどうすればよいですか?
AJSON.stringify で簡易比較するか、再帰的な deepEqual 関数を自作します。lodash の _.isEqual を使えば最も手軽にディープ比較ができます。
Q2 つの配列の差分(片方にしかない要素)を取得するには?
Aa.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 で高速に計算

「何を比較したいのか」(順序込み / 順序無視 / 重複考慮 / ネスト対応)を明確にし、最適な方法を選んでください。