Object比較にJSON.stringify()は使えないのでisEqual関数を作成した

Object比較にJSON.stringify()は使えない

オブジェクトの比較にJSON.stringify()を使用しているケースが多いがオブジェクトの中身も順序が異なるとJSON.stringify()を使用するとfalseになることは現在ではよく知られている。

var objA = { x: 5, y: 6 };
var objB = { y: 6, x: 5 };

console.log(JSON.stringify(objA) === JSON.stringify(objB));
// // => false

配列比較にString()は使えない

配列もオブジェクトと同様に中身の順序が異なるとString()で比較するとfalseになるので注意が必要だ。

var arrA = ['foo', 'bar']
var arrB = ['bar', 'foo']

console.log(arrA === arrB);
// => false

console.log(String(arrA) === String(arrB));
// => false

isEqual関数作成方法

前述のオブジェクトや配列の中身の順序が異なっていても比較できるようにするためにはLodash(Underscore.js)の_.isEqual()を使用するよう書かれているサイトが多い。

しかし、比較のためだけにLodash(Underscore.js)を読み込むのは無駄なのでisEqual関数を作成した。

作成したisEqual関数はFunctionの比較はできない簡易版だが通常の値の比較であれば問題なく使えるはずだ。(IE11でも使用可能)

function isEqual(a, b) {
  var tmpA = [];
  var tmpB = [];
  var isNaN = function(a, b) {
  return typeof a === 'number' && a !== a &&
         typeof b === 'number' && b !== b;
  }

  if (isNaN(a, b)) return true;
  if (Array.isArray(a) !== Array.isArray(b)) return false;
  if ((typeof a === 'number' || typeof b === 'number') ||
       typeof a === 'string' || typeof b === 'string' ||
       typeof a === 'boolean' || typeof b === 'boolean' ||
       a === null || b === null ||
       a === undefined || b === undefined
  ) {
    return a === b;
  }
  if (Array.isArray(a) && Array.isArray(b)) {
    a = a.sort();
    b = b.sort();
  }
  for (var v in a) {
    tmpA.push([v, a[v]]);
  }
  for (var v in b) {
    tmpB.push([v, b[v]]);
  }
  var resultA = tmpA.sort(function(a, b) {
    return a[1] - b[1];
  });
  var resultB = tmpB.sort(function(a, b) {
    return a[1] - b[1];
  });
  var flag = true;
  resultA.forEach(function(v, i) {
    if (v[1] !== resultB[i][1]) {
      flag = false;
      return flag;
    }
  });
  return flag;
}

var objA = { x: 5, y: 6 };
var objB = { y: 6, x: 5 };

console.log(JSON.stringify(objA) === JSON.stringify(objB));
// // => false

console.log(isEqual(objA, objB));
// => true

var arrA = ['foo', 'bar']
var arrB = ['bar', 'foo']

console.log(String(arrA) === String(arrB));
// => false

console.log(isEqual(arrA, arrB));
// => true

…いや、やはりこれだと問題があるので_.isEqual()を使用したほうが良さそうだ。