オブジェクトの比較方法
JavaScript でオブジェクトを比較する際、単純な === では期待通りに動作しません。オブジェクトは参照型であるため、内容が同じでも別のオブジェクトは等しくないと判定されます。
参照の比較と値の比較
=== はオブジェクトの参照(メモリ上の位置)を比較します。内容が同じでも、別々に作られたオブジェクトは等しくありません。
const obj1 = { name: "田中" };
const obj2 = { name: "田中" };
const obj3 = obj1;
console.log(obj1 === obj2); // false(内容は同じだが別オブジェクト)
console.log(obj1 === obj3); // true(同じ参照)参照の比較(===)
同じオブジェクトを指しているかを確認する
値の比較(内容比較)
オブジェクトの中身が等しいかを確認する
JSON.stringify() による比較
シンプルなオブジェクトであれば、JSON 文字列に変換して比較する方法があります。
const obj1 = { name: "田中", age: 25 };
const obj2 = { name: "田中", age: 25 };
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // trueただし、この方法にはいくつかの問題があります。
プロパティの順序
プロパティの順序が異なると、内容が同じでも false になります。
特殊な値
undefined、関数、Symbol は JSON.stringify() で消えるため、正しく比較できません。
const a = { x: 1, y: 2 };
const b = { y: 2, x: 1 };
// 内容は同じだが順序が違う
console.log(JSON.stringify(a)); // '{"x":1,"y":2}'
console.log(JSON.stringify(b)); // '{"y":2,"x":1}'
console.log(JSON.stringify(a) === JSON.stringify(b)); // false自作の比較関数
プロパティを一つずつ比較する関数を作成することで、より正確な比較ができます。
function shallowEqual(obj1, obj2) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) {
return false;
}
return keys1.every(key => obj1[key] === obj2[key]);
}
const a = { x: 1, y: 2 };
const b = { y: 2, x: 1 };
console.log(shallowEqual(a, b)); // trueこの関数はシャローな比較(1階層のみ)です。ネストしたオブジェクトは参照で比較されます。
ディープな比較
ネストしたオブジェクトも含めて比較するには、再帰的な処理が必要です。
function deepEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== "object" || typeof obj2 !== "object") {
return false;
}
if (obj1 === null || obj2 === null) return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
return keys1.every(key => deepEqual(obj1[key], obj2[key]));
}
const a = { user: { name: "田中" } };
const b = { user: { name: "田中" } };
console.log(deepEqual(a, b)); // true配列の比較
配列もオブジェクトなので、同様の問題があります。
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false
// 要素ごとに比較
console.log(
arr1.length === arr2.length &&
arr1.every((val, i) => val === arr2[i])
); // trueライブラリの活用
実際のプロジェクトでは、Lodash の _.isEqual() などのライブラリを使用するのが一般的です。エッジケースも含めて正しく処理してくれます。
// Lodash を使用した場合
// import _ from 'lodash';
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
// _.isEqual(obj1, obj2); // trueオブジェクトの比較は意外と複雑なので、要件に合った方法を選択することが重要です。