我有两个对象:oldObj和newObj。
oldObj中的数据用于填充表单,而newObj是用户更改该表单中的数据并提交的结果。
这两个物体都很深。它们具有对象或对象数组等属性-它们可以有n层深,因此diff算法需要递归。
现在我不仅需要弄清楚从oldObj到newObj更改了什么(如添加/更新/删除),而且还需要知道如何最好地表示它。
到目前为止,我的想法只是构建一个通用的deepdiffbetweenobjects方法,该方法将返回窗体上的对象{add:{…},upd:{…},del:{…但我转念一想:以前一定有人需要它。
所以…有没有人知道一个库或一段代码可以做到这一点,并且可能有更好的方式来表示差异(以一种仍然是JSON可序列化的方式)?
更新:
我想到了一个更好的方法来表示更新的数据,通过使用与newObj相同的对象结构,但将所有属性值转换为窗体上的对象:
{type: '<update|create|delete>', data: <propertyValue>}
如果newObj。prop1 = 'new value'和oldObj。prop1 = 'old value'它将设置returnbj。Prop1 = {type: 'update', data: 'new value'}
更新2:
当我们处理数组的属性时,它会变得非常麻烦,因为数组[1,2,3]应该被计算为等于[2,3,1],这对于基于值的类型的数组(如string, int和bool)来说足够简单,但是当涉及到引用类型的数组(如对象和数组)时就很难处理了。
数组的示例应该是相等的:
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
不仅要检查这种类型的深度值相等相当复杂,而且要找出一种表示可能发生的变化的好方法。
我只是使用ramda,为了解决同样的问题,我需要知道在新对象中有什么改变。这是我的设计。
const oldState = {id:'170',name:'Ivab',secondName:'Ivanov',weight:45};
const newState = {id:'170',name:'Ivanko',secondName:'Ivanov',age:29};
const keysObj1 = R.keys(newState)
const filterFunc = key => {
const value = R.eqProps(key,oldState,newState)
return {[key]:value}
}
const result = R.map(filterFunc, keysObj1)
结果是属性的名称和状态。
[{"id":true}, {"name":false}, {"secondName":true}, {"age":false}]
下面的方法将创建一个只更改字段的新对象
const findDiff = (obj1, obj2) => {
const isNativeType1 = typeof obj1 !== "object";
const isNativeType2 = typeof obj2 !== "object";
if (isNativeType1 && isNativeType2) {
return obj1 === obj2 ? null : obj2;
}
if (isNativeType1 && !isNativeType2) {
return obj2;
}
if (!isNativeType1 && isNativeType2) {
return obj2;
}
const isArray1 = Array.isArray(obj1);
const isArray2 = Array.isArray(obj2);
if (isArray1 && isArray2) {
const firstLenght = obj1.length;
const secondLenght = obj2.length;
const hasSameLength = firstLenght === secondLenght;
if (!hasSameLength) return obj2;
let hasChange = false;
for (let index = 0; index < obj1.length; index += 1) {
const element1 = obj1[index];
const element2 = obj2[index];
const changed = findDiff(element1, element2);
if (changed) {
hasChange = true;
}
}
return hasChange ? obj2 : null;
}
if (isArray1 || isArray2) return obj2;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
const hasSameKeys = keys1.length === keys2.length;
if (!hasSameKeys) {
const retObj = { ...obj2 };
for (let index = 0; index < keys1.length; index += 1) {
const key = keys1[index];
if (!keys2.includes(key)) {
retObj[key] = null;
// eslint-disable-next-line no-continue
continue;
}
delete retObj[key];
}
return retObj;
}
let hasChange = false;
const retObj = {};
for (let index = 0; index < keys1.length; index += 1) {
const key = keys1[index];
const element1 = obj1[key];
const element2 = obj2[key];
const changed = findDiff(element1, element2);
if (changed) {
hasChange = true;
}
if (changed) {
retObj[key] = changed;
}
}
return hasChange ? retObj : null;
};
console.log(
JSON.stringify(findDiff(
{
a: 1,
b: 2,
c: {
a: ['1', 'b', { a: 'b', c: false }, true],
},
},
{
a: 1,
b: 2,
c: {
a: ['1','b', { a: 'b', c: true }, true],
},
}
), null, 2)
);
我知道我迟到了,但我需要类似的东西,上面的答案没有帮助。
我使用了Angular的$watch函数来检测变量的变化。我不仅需要知道变量上的属性是否发生了变化,而且还需要确保发生变化的属性不是一个临时的计算字段。换句话说,我想忽略某些属性。
代码如下:
function diff(obj1,obj2,exclude) {
var r = {};
if (!exclude) exclude = [];
for (var prop in obj1) {
if (obj1.hasOwnProperty(prop) && prop != '__proto__') {
if (exclude.indexOf(obj1[prop]) == -1) {
// check if obj2 has prop
if (!obj2.hasOwnProperty(prop)) r[prop] = obj1[prop];
// check if prop is object and
// NOT a JavaScript engine object (i.e. __proto__), if so, recursive diff
else if (obj1[prop] === Object(obj1[prop])) {
var difference = diff(obj1[prop], obj2[prop]);
if (Object.keys(difference).length > 0) r[prop] = difference;
}
// check if obj1 and obj2 are equal
else if (obj1[prop] !== obj2[prop]) {
if (obj1[prop] === undefined)
r[prop] = 'undefined';
if (obj1[prop] === null)
r[prop] = null;
else if (typeof obj1[prop] === 'function')
r[prop] = 'function';
else if (typeof obj1[prop] === 'object')
r[prop] = 'object';
else
r[prop] = obj1[prop];
}
}
}
}
return r;
}
https://jsfiddle.net/rv01x6jo/
下面是如何使用它:
// To only return the difference
var difference = diff(newValue, oldValue);
// To exclude certain properties
var difference = diff(newValue, oldValue, [newValue.prop1, newValue.prop2, newValue.prop3]);
希望这能帮助到一些人。
从sbgoran的答案中得到扩展和简化的函数。
这允许深度扫描和发现数组的相似性。
var result = objectDifference({
a:'i am unchanged',
b:'i am deleted',
e: {a: 1,b:false, c: null},
f: [1,{a: 'same',b:[{a:'same'},{d: 'delete'}]}],
g: new Date('2017.11.25'),
h: [1,2,3,4,5]
},
{
a:'i am unchanged',
c:'i am created',
e: {a: '1', b: '', d:'created'},
f: [{a: 'same',b:[{a:'same'},{c: 'create'}]},1],
g: new Date('2017.11.25'),
h: [4,5,6,7,8]
});
console.log(result);
function objectDifference(obj1, obj2){
if((dataType(obj1) !== 'array' && dataType(obj1) !== 'object') || (dataType(obj2) !== 'array' && dataType(obj2) !== 'object')){
var type = '';
if(obj1 === obj2 || (dataType(obj1) === 'date' && dataType(obj2) === 'date' && obj1.getTime() === obj2.getTime()))
type = 'unchanged';
else if(dataType(obj1) === 'undefined')
type = 'created';
if(dataType(obj2) === 'undefined')
type = 'deleted';
else if(type === '') type = 'updated';
return {
type: type,
data:(obj1 === undefined) ? obj2 : obj1
};
}
if(dataType(obj1) === 'array' && dataType(obj2) === 'array'){
var diff = [];
obj1.sort(); obj2.sort();
for(var i = 0; i < obj2.length; i++){
var type = obj1.indexOf(obj2[i]) === -1?'created':'unchanged';
if(type === 'created' && (dataType(obj2[i]) === 'array' || dataType(obj2[i]) === 'object')){
diff.push(
objectDifference(obj1[i], obj2[i])
);
continue;
}
diff.push({
type: type,
data: obj2[i]
});
}
for(var i = 0; i < obj1.length; i++){
if(obj2.indexOf(obj1[i]) !== -1 || dataType(obj1[i]) === 'array' || dataType(obj1[i]) === 'object')
continue;
diff.push({
type: 'deleted',
data: obj1[i]
});
}
} else {
var diff = {};
var key = Object.keys(obj1);
for(var i = 0; i < key.length; i++){
var value2 = undefined;
if(dataType(obj2[key[i]]) !== 'undefined')
value2 = obj2[key[i]];
diff[key[i]] = objectDifference(obj1[key[i]], value2);
}
var key = Object.keys(obj2);
for(var i = 0; i < key.length; i++){
if(dataType(diff[key[i]]) !== 'undefined')
continue;
diff[key[i]] = objectDifference(undefined, obj2[key[i]]);
}
}
return diff;
}
function dataType(data){
if(data === undefined || data === null) return 'undefined';
if(data.constructor === String) return 'string';
if(data.constructor === Array) return 'array';
if(data.constructor === Object) return 'object';
if(data.constructor === Number) return 'number';
if(data.constructor === Boolean) return 'boolean';
if(data.constructor === Function) return 'function';
if(data.constructor === Date) return 'date';
if(data.constructor === RegExp) return 'regex';
return 'unknown';
}