CODEGURU

Как сравнить объекты в Javascript

В Javascript нельзя просто взять и сравнить два объекта привычным способом: Obj1 === Obj2. Такое выражение всегда вернёт false. Всё дело в том, что объекты являются ссылочным типом данных и значение в них передаётся по ссылке. Следовательно, при сравнении объектов мы, по сути, сравниваем то, куда указывает их ссылка. А у разных объектов, даже если их содержимое идентично, ссылка указывает в разные места. Так как же нам сравнить объекты? Тут у нас есть 3 способа.

1. С помощью JSON.stringify

Тут всё довольно просто. Мы конвертируем объекты в строку и сравниваем полученные строки.

const object1 = {
  title: "Title",
  id: 1
};

const object2 = {
  title: "Title",
  id: 1
};

const object3 = {
  title: "Another title",
  id: 3
};

console.log(JSON.stringify(object1) === JSON.stringify(object2)); // true
console.log(JSON.stringify(object2) === JSON.stringify(object3)); // false
console.log(JSON.stringify(object1) === JSON.stringify(object3)); // false

Однако у этого метода есть некоторые ограничения. Например, если свойства объектов идут не в одном порядке, то сравнение вернёт false.

const object1 = {
  title: "Title",
  id: 1
};

const object2 = {
  id: 1
  title: "Title",
};

console.log(JSON.stringify(object1) === JSON.stringify(object2)); // false

2. С помощью библиотеки lodash

Можно вообще не париться и просто взять готовый инструмент. В библиотеке lodash есть метод _.isEqual, который без проблем сравнивает объекты.

const object1 = {
  title: "Title",
  id: 1
};

const object2 = {
  id: 1
  title: "Title",
};

console.log(_.isEqual(object1, object2)); // true

3. Написать свою функцию для сравнения объектов

Это, конечно, совершенно необязательно. Но для поддержания тонуса почему бы не попрактиковаться.

Первое, что приходит в голову — это составить список ключей объектов и сравнить их значения.

function isEqual(object1, object2) {
  const props1 = Object.getOwnPropertyNames(object1);
  const props2 = Object.getOwnPropertyNames(object2);

  if (props1.length !== props2.length) {
    return false;
  }

  for (let i = 0; i < props1.length; i += 1) {
    const prop = props1[i];

    if (object1[prop] !== object2[prop]) {
      return false;
    }
  }

  return true;
}

const object1 = {
  title: "Title",
  id: 1
};

const object2 = {
  id: 1
  title: "Title",
};

console.log(isEqual(object1, object2)); // true

Работает. Но есть одна проблема. Если у нас будет вложенный объект, то перестанет работать.

const object1 = {
  title: "Title",
  id: 1,
  price: {
    base: 100,
    vat: 20
  }
};

const object2 = {
  id: 1
  title: "Title",
  price: {
    base: 100,
    vat: 20
  }
};

console.log(isEqual(object1, object2)); // false

Происходит это, потому что мы просто сравниваем значения ключей объектов. А когда значение само является объектом, то, естественно, простое сравнение всегда возвращает false. Нужно как-то это предусмотреть.

function isEqual(object1, object2) {
  const props1 = Object.getOwnPropertyNames(object1);
  const props2 = Object.getOwnPropertyNames(object2);

  if (props1.length !== props2.length) {
    return false;
  }

  for (let i = 0; i < props1.length; i += 1) {
    const prop = props1[i];
    const bothAreObjects = typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object';

    if ((!bothAreObjects && (object1[prop] !== object2[prop]))
    || (bothAreObjects && !isEqual(object1[prop], object2[prop]))) {
      return false;
    }
  }

  return true;
}

Вот теперь работает как надо и нам не страшны вложенные объекты. Если значения ключей являются объектами, то мы рекурсивно вызываем нашу функцию isEqual.