У меня есть два вложенных объекта 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: глубокое рекурсивное сравнение: объекты и свойства
хорошая точка зрения. Нет, поэтому равенство может иметь новый ключ, установленный на false
. Обновление с этим пунктом. Спасибо.
Участник ссылается на эту ссылку в своем вопросе, и у него есть массивы глубоко вложенных объектов в качестве входных данных, поэтому я подумал, что должен сослаться на свой ответь здесь
Вы можете перебрать все ключи и проверить вложенные объекты, если оба значения являются объектами.
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
для большей совместимости может помочь.
может как Object.assign(Array.from(new Set( [].concat(Object.keys(a)).concat(Object.keys(b)))
Вы можете создать объект 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))
Спасибо, только что обновил код, предполагая, что у вас также могут быть новые свойства.
что, если obj1 имеет некоторый ключ с дочерним obj в качестве его значения, а obj2 не имеет того же ключа, тогда метод будет вызываться с помощью compare(obj1, undefined), что выдаст ошибку в obj2[key]
@adiga не уверен, но в комментариях к вопросу упоминается об этом, а также в обновленном вопросе нельзя предполагать, что дополнительный кет всегда имеет строковое значение.
@AZ_ обновлено. Не уверен, что это не удастся для любого сценария
Вы можете использовать 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.
Просто, если предположить, что свойство является пустым объектом, таким как {}
, оно не делает сравнения.
Хорошо, теперь он обрабатывает вложенный одинокий объект prop11 = {}
, но если у вас есть одинаковые prop12 = {}
в обоих, вы получите {}
в качестве результата для ключевого слова prop12
вместо bool
. См. здесь jsfiddle.net/gpu20nwy
Это работает, пожалуйста, обновите решение, чтобы я мог его принять!
Рекурсивный пример,
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 Я внес изменение, попробуйте сейчас.
Вот решение, которое я недавно сделал, которое может справиться с той же проблемой, и для него требуется необязательный ключ для настройки строгости сравнения. Полезно, когда ваш сервер отправляет значение в виде числа, но ожидает, что это значение будет возвращено в виде строки. Мы использовали сравнение 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;
}
Как сейчас написано, ваш ответ неясен. Пожалуйста, редактировать, чтобы добавить дополнительную информацию, которая поможет другим понять, как это относится к заданному вопросу. Дополнительную информацию о том, как писать хорошие ответы, можно найти в справочном центре.
Будут ли оба объекта всегда иметь свойства точного соответствия?