Сравните вложенные объекты в JavaScript и верните равенство ключей

У меня есть два вложенных объекта obj1 и obj2, и я хочу сравнить их и рекурсивно вернуть объект, который для каждого вложенного ключа имеет логический флаг, подобный равенству.

Так что за данный obj1 лайк

obj1 = {
  prop1: 1,
  prop2: "foo",
  prop3: {
    prop4: 2,
    prop5: "bar" 
  }
}

и лайк obj2

obj2 = {
      prop1: 3,
      prop2: "foo",
      prop3: {
        prop4: 2,
        prop5: "foobar" 
      },
      prop6: "new"
    }

он должен вернуться

equality = {
     prop1: false,
     prop2: true,
     prop3 : {
       prop4: true,
       prop5: false
     },
     prop6: false
   }

Если у объекта есть новое свойство, например obj2.prop6, то равенство будет equality.prop6 = false.

Для не вложенных объектов простое решение для сравнения ключей здесь Получить свойство разницы между двумя объектами в javascript В то время как для рекурсивного сравнения вложенных объектов показано здесь JavaScript: глубокое рекурсивное сравнение: объекты и свойства

Будут ли оба объекта всегда иметь свойства точного соответствия?

holydragon 09.04.2019 12:59

хорошая точка зрения. Нет, поэтому равенство может иметь новый ключ, установленный на false. Обновление с этим пунктом. Спасибо.

loretoparisi 09.04.2019 13:00

Участник ссылается на эту ссылку в своем вопросе, и у него есть массивы глубоко вложенных объектов в качестве входных данных, поэтому я подумал, что должен сослаться на свой ответь здесь

Nur 22.04.2021 19:59
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
11
3
10 182
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Вы можете перебрать все ключи и проверить вложенные объекты, если оба значения являются объектами.

const isObject = v => v && typeof v === 'object';

function getDifference(a, b) {
    return Object.assign(...Array.from(
        new Set([...Object.keys(a), ...Object.keys(b)]),
        k => ({ [k]: isObject(a[k]) && isObject(b[k])
            ? getDifference(a[k], b[k])
            : a[k] === b[k]
        })
    ));
}

var obj1 = { prop1: 1, prop2: "foo", prop3: { prop4: 2, prop5: "bar" } },
    obj2 = { prop1: 3, prop2: "foo", prop3: { prop4: 2, prop5: "foobar" }, prop6: "new" };

console.info(getDifference(obj1, obj2));

Спасибо! Это решение работает в любых условиях, даже если свойство является пустым объектом, например prop7 = {}. Возможно, избегание нотации ECMA6 Spread для большей совместимости может помочь.

loretoparisi 09.04.2019 14:30

может как Object.assign(Array.from(new Set( [].concat(Object.keys(a)).concat(Object.keys(b)))

loretoparisi 09.04.2019 14:35

Вы можете создать объект merged, который будет иметь ключи обоих объектов. Прокрутите этот объект и сравните значения для obj1 и obj2 для каждого ключа. Если свойство является объектом, рекурсивно сравните свойства. Это будет работать для любого уровня вложенности. Поскольку свойства могут отсутствовать в любом из объектов, добавляется параметр по умолчанию= {}.

const obj1 = {prop1:1,prop2:"foo",prop3:{prop4:2,prop5:"bar"},prop7:{pro8:"only in 1"}},
      obj2 = {prop1:3,prop2:"foo",prop3:{prop4:2,prop5:"foobar"}, prop6: "only in 2"};
    
const isObject = val => typeof val === 'object' && val // required for "null" comparison

function compare(obj1 = {}, obj2 = {}) {
  const output = {},
        merged = { ...obj1, ...obj2 }; // has properties of both

  for (const key in merged) {
    const value1 = obj1[key],
          value2 = obj2[key];

    if (isObject(value1) || isObject(value2))
      output[key] = compare(value1, value2) // recursively call
    else
      output[key] = value1 === value2
  }
  
  return output;
}

console.info(compare(obj1, obj2))

Спасибо, только что обновил код, предполагая, что у вас также могут быть новые свойства.

loretoparisi 09.04.2019 13:05

что, если obj1 имеет некоторый ключ с дочерним obj в качестве его значения, а obj2 не имеет того же ключа, тогда метод будет вызываться с помощью compare(obj1, undefined), что выдаст ошибку в obj2[key]

AZ_ 09.04.2019 13:09

@adiga не уверен, но в комментариях к вопросу упоминается об этом, а также в обновленном вопросе нельзя предполагать, что дополнительный кет всегда имеет строковое значение.

AZ_ 09.04.2019 13:18

@AZ_ обновлено. Не уверен, что это не удастся для любого сценария

adiga 09.04.2019 13:27
Ответ принят как подходящий

Вы можете использовать reduce для создания нового объекта и другой метод get для получения вложенных реквизитов из другого объекта с помощью string и сравнения его с текущим значением реквизита в первом объекте.

const obj1 = { prop1: 1, prop2: "foo", prop3: { prop4: 2, prop5: "bar" } }
const obj2 = { prop1: 3, prop2: "foo", prop3: { prop4: 2, prop5: "foobar" } }

function get(obj, path) {
  return path.split('.').reduce((r, e) => {
    if (!r) return r
    else return r[e] || undefined
  }, obj)
}

function compare(a, b, prev = "") {
  return Object.keys(a).reduce((r, e) => {
    const path = prev + (prev ? '.' + e : e);
    const value = a[e] === get(b, path);
    r[e] = typeof a[e] === 'object' ? compare(a[e], b, path) : value
    return r;
  }, {})
}

const result = compare(obj1, obj2);
console.info(result)

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

const obj1 = {"prop1":1,"prop2":"foo","prop3":{"prop4":2,"prop5":"bar"},"prop7":{"prop9":{"prop10":"foo"}}}
const obj2 = {"prop1":3,"prop2":"foo","prop3":{"prop4":2,"prop5":"foobar"},"prop6":"new","prop7":{"foo":"foo","bar":{"baz":"baz"}}}

function get(obj, path) {
  return path.split('.').reduce((r, e) => {
    if (!r) return r;
    else return r[e] || undefined;
  }, obj);
}

function isEmpty(o) {
  if (typeof o !== 'object') return true;
  else return !Object.keys(o).length;
}

function build(a, b, o = null, prev = '') {
  return Object.keys(a).reduce(
    (r, e) => {
      const path = prev + (prev ? '.' + e : e);
      const bObj = get(b, path);
      const value = a[e] === bObj;

      if (typeof a[e] === 'object') {
        if (isEmpty(a[e]) && isEmpty(bObj)) {
          if (e in r) r[e] = r[e];
          else r[e] = true;
        } else if (!bObj && isEmpty(a[e])) {
          r[e] = value;
        } else {
          r[e] = build(a[e], b, r[e], path);
        }
      } else {
        r[e] = value;
      }
      return r;
    },
    o ? o : {}
  );
}

function compare(a, b) {
  const o = build(a, b);
  return build(b, a, o);
}

const result = compare(obj1, obj2);
console.info(result)

Я думаю, что это лучшее решение, поскольку оно поддерживает все последние версии ECMAScript.

loretoparisi 09.04.2019 14:23

Просто, если предположить, что свойство является пустым объектом, таким как {}, оно не делает сравнения.

loretoparisi 09.04.2019 14:29

Хорошо, теперь он обрабатывает вложенный одинокий объект prop11 = {}, но если у вас есть одинаковые prop12 = {} в обоих, вы получите {} в качестве результата для ключевого слова prop12 вместо bool. См. здесь jsfiddle.net/gpu20nwy

loretoparisi 09.04.2019 14:50

Это работает, пожалуйста, обновите решение, чтобы я мог его принять!

loretoparisi 09.04.2019 15:26

Рекурсивный пример,

var obj1 = {
        prop1: 1,
        prop2: "foo",
        prop3: {
            prop4: 2,
            prop5: "bar"
        },
        prop7: {},
    }

    var obj2 = {
        prop1: 3,
        prop2: "foo",
        prop3: {
            prop4: 2,
            prop5: "foobar"
        },
        prop6: "new",
        prop7: {},
        prop8: {},
    }

    var result = {};

    function compare(obj1, obj2, obj_) {
        for (let k in obj1) {
            var type = typeof obj1[k];
            if (type === 'object') {
                obj_[k] = {};
                if (!obj2[k]){
                    obj_[k] = false;
                }else if ((Object.entries(obj1[k]).length === 0 && obj1[k].constructor === Object) && (Object.entries(obj2[k]).length === 0 && obj2[k].constructor === Object)) {
                    obj_[k] = true;
                } else {
                    compare(obj1[k], obj2[k], obj_[k]);
                }
            } else {
                obj_[k] = (obj1[k] === obj2[k]);
            }

        }
    }

    if (Object.keys(obj1).length < Object.keys(obj2).length) { //check if both objects vary in length.
        var tmp = obj1;
        obj1 = obj2;
        obj2 = tmp;
    }

    compare(obj1, obj2, result);

    console.info(result);

Спасибо. Если вы попробуете этот случай — jsfiddle.net/47fcqtom, вы получите {} в результатах для свойств типа object, которые принадлежат обоим объектам и являются {}.

loretoparisi 09.04.2019 14:57

@loretoparisi Я внес изменение, попробуйте сейчас.

Shoyeb Sheikh 09.04.2019 15:36

Вот решение, которое я недавно сделал, которое может справиться с той же проблемой, и для него требуется необязательный ключ для настройки строгости сравнения. Полезно, когда ваш сервер отправляет значение в виде числа, но ожидает, что это значение будет возвращено в виде строки. Мы использовали сравнение JSON.stringify, но оно немного грубое и не может учитывать, когда объекты одинаковы, но ключи находятся в другом порядке.

public objectsAreTheSame(objA: {[key: string]: any}, objB: {[key: string]: any}, isStrict = false): boolean {
        let areTheSame = true;

        const strictLevel = isStrict ? 'isStrict' : 'isNotStrict';

        const valuesDoNotMatch = {
            'isStrict': (a, b) => a !== b,
            'isNotStrict': (a, b) => a != b
        };

        const isObject = (a, b) => typeof a === 'object' && !Array.isArray(a) && (!!a && !!b);

        const compareArrays = (a, b) => {
            if (a.length === b.length) {
                a.sort();
                b.sort();

                a.forEach((ele, idx) => compareValues(ele, b[idx]));
            } else {
                areTheSame = false;
            }
        };

        const compareValues = (a, b) => {
            if (Array.isArray(a)) {
                compareArrays(a, b);
            } else if (!isObject(a, b) && valuesDoNotMatch[strictLevel](a, b)) {
                areTheSame = false;
            } else if (isObject(a, b) && !this.objectsAreTheSame(a, b, isStrict)) {
                areTheSame = false;
            }
        };

        const keysA = Object.keys(objA);
        const keysB = Object.keys(objB);

        if (keysA.length !== keysB.length) return false;

        for (let key of keysA) compareValues(objA[key], objB[key]);

        return areTheSame;
    }

Почему бы не преобразовать объекты в json и не сравнить строки json?

const areEqual(obj1, obj2) {
  let json1 = JSON.stringify(obj1);
  let json2 = JSON.stringify(obj2);

  return json1 === json2;
}

Как сейчас написано, ваш ответ неясен. Пожалуйста, редактировать, чтобы добавить дополнительную информацию, которая поможет другим понять, как это относится к заданному вопросу. Дополнительную информацию о том, как писать хорошие ответы, можно найти в справочном центре.

Community 22.01.2022 16:38

Другие вопросы по теме