我有两个对象:oldObj和newObj.
oldObj
newObj
中的数据oldObj用于填充表单,newObj是用户更改此表单中的数据并提交它的结果。
两个物体都很深,即。它们具有对象或对象数组等属性 - 它们可以是 n 级深,因此差异算法需要是递归的。
现在我不仅需要弄清楚从 to 发生了什么变化(如添加/更新/删除)oldObj,newObj还需要弄清楚如何最好地表示它。
到目前为止,我的想法是构建一个genericDeepDiffBetweenObjects可以在表单上返回一个对象的方法,{add:{...},upd:{...},del:{...}}但后来我想:以前肯定有人需要这个。
genericDeepDiffBetweenObjects
{add:{...},upd:{...},del:{...}}
那么......有没有人知道一个库或一段代码可以做到这一点,并且可能有更好的方式来表示差异(以一种仍然是 JSON 可序列化的方式)?
我想到了一种更好的方法来表示更新的数据,方法是使用与 相同的对象结构newObj,但将所有属性值转换为表单上的对象:
{type: '<update|create|delete>', data: <propertyValue>}
所以如果newObj.prop1 = 'new value'它oldObj.prop1 = 'old value'会设置returnObj.prop1 = {type: 'update', data: 'new value'}
newObj.prop1 = 'new value'
oldObj.prop1 = 'old value'
returnObj.prop1 = {type: 'update', data: 'new value'}
当我们处理数组的属性时,它会变得非常棘手,因为数组[1,2,3]应该被视为等于[2,3,1]引用类型的数组,如对象和数组。
[1,2,3]
[2,3,1]
应该找到相等的示例数组:
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
检查这种类型的深度值相等不仅非常复杂,而且要找出一种表示可能发生的变化的好方法。
我写了一个小类,做你想做的,你可以在这里测试。
唯一与你的提议不同的是我不考虑
[1,[{c: 1},2,3],{a:'hey'}]
和
[{a:'hey'},1,[3,{c: 1},2]]
相同,因为我认为如果数组元素的顺序不同,则数组不相等。当然,如果需要,这可以更改。此外,此代码可以进一步增强,以将函数作为参数,用于根据传递的原始值以任意方式格式化 diff 对象(现在这项工作由“compareValues”方法完成)。
var deepDiffMapper = function () { return { VALUE_CREATED: 'created', VALUE_UPDATED: 'updated', VALUE_DELETED: 'deleted', VALUE_UNCHANGED: 'unchanged', map: function(obj1, obj2) { if (this.isFunction(obj1) || this.isFunction(obj2)) { throw 'Invalid argument. Function given, object expected.'; } if (this.isValue(obj1) || this.isValue(obj2)) { return { type: this.compareValues(obj1, obj2), data: obj1 === undefined ? obj2 : obj1 }; } var diff = {}; for (var key in obj1) { if (this.isFunction(obj1[key])) { continue; } var value2 = undefined; if (obj2[key] !== undefined) { value2 = obj2[key]; } diff[key] = this.map(obj1[key], value2); } for (var key in obj2) { if (this.isFunction(obj2[key]) || diff[key] !== undefined) { continue; } diff[key] = this.map(undefined, obj2[key]); } return diff; }, compareValues: function (value1, value2) { if (value1 === value2) { return this.VALUE_UNCHANGED; } if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) { return this.VALUE_UNCHANGED; } if (value1 === undefined) { return this.VALUE_CREATED; } if (value2 === undefined) { return this.VALUE_DELETED; } return this.VALUE_UPDATED; }, isFunction: function (x) { return Object.prototype.toString.call(x) === '[object Function]'; }, isArray: function (x) { return Object.prototype.toString.call(x) === '[object Array]'; }, isDate: function (x) { return Object.prototype.toString.call(x) === '[object Date]'; }, isObject: function (x) { return Object.prototype.toString.call(x) === '[object Object]'; }, isValue: function (x) { return !this.isObject(x) && !this.isArray(x); } } }(); var result = deepDiffMapper.map({ 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') }, { 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') }); console.log(result);