Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
414 views
in Technique[技术] by (71.8m points)

javascript - 两个对象之间的一般深度差异(Generic deep diff between two objects)

I have two objects: oldObj and newObj .

(我有两个对象: oldObjnewObj 。)

The data in oldObj was used to populate a form and newObj is the result of the user changing data in this form and submitting it.

(oldObj中的数据用于填充表单,而newObj是用户更改此表单中的数据并提交后的结果。)

Both objects are deep, ie.

(两个物体都很深,即。)

they have properties that are objects or arrays of objects etc - they can be n levels deep, thus the diff algorithm needs to be recursive.

(它们具有对象或对象数组等属性-它们的深度可以为n级,因此diff算法需要递归。)

Now I need to not just figure out what was changed (as in added/updated/deleted) from oldObj to newObj , but also how to best represent it.

(现在,我不仅需要弄清楚从oldObjnewObj更改(如添加/更新/删除),还需要如何最好地表示它。)

So far my thoughts was to just build a genericDeepDiffBetweenObjects method that would return an object on the form {add:{...},upd:{...},del:{...}} but then I thought: somebody else must have needed this before.

(到目前为止,我的想法只是建立一个genericDeepDiffBetweenObjects方法,该方法将以{add:{...},upd:{...},del:{...}}的形式返回对象,但后来我想到:有人否则一定需要这个。)

So... does anyone know of a library or a piece of code that will do this and maybe have an even better way of representing the difference (in a way that is still JSON serializable)?

(那么...是否有人知道可以做到这一点的库或一段代码,也许有一种更好的方式来表示差异(以仍可序列化JSON的方式)?)

Update:(更新:)

I have thought of a better way to represent the updated data, by using the same object structure as newObj , but turning all property values into objects on the form:

(我想过一种更好的方式来表示更新的数据,方法是使用与newObj相同的对象结构,但将所有属性值都转换为表单上的对象:)

{type: '<update|create|delete>', data: <propertyValue>}

So if newObj.prop1 = 'new value' and oldObj.prop1 = 'old value' it would set returnObj.prop1 = {type: 'update', data: 'new value'}

(因此,如果newObj.prop1 = 'new value'oldObj.prop1 = 'old value' ,则会设置returnObj.prop1 = {type: 'update', data: 'new value'})

Update 2:(更新2:)

It gets truely hairy when we get to properties that are arrays, since the array [1,2,3] should be counted as equal to [2,3,1] , which is simple enough for arrays of value based types like string, int & bool, but gets really difficult to handle when it comes to arrays of reference types like objects and arrays.

(当我们进入数组属性时,它确实变得毛骨悚然,因为数组[1,2,3]应该算作等于[2,3,1] ,对于基于值的类型(如字符串, int&bool,但是当涉及到引用类型的数组(例如对象和数组)时,就变得很难处理。)

Example arrays that should be found equal:

(应该找到相等的示例数组:)

[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]

Not only is it quite complex to check for this type of deep value equality, but also to figure out a good way to represent the changes that might be.

(检查这种类型的深度值相等不仅很复杂,而且要找到表示可能发生的更改的好方法。)

  ask by Martin Jespersen translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I wrote a little class that is doing what you want, you can test it here .

(我写了一个小班,正在做您想做的事,您可以在这里进行测试。)

Only thing that is different from your proposal is that I don't consider [1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]] to be same, because I think that arrays are not equal if order of their elements is not same.

(唯一与您的建议不同的是,我不考虑[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]是相同的,因为如果它们的元素顺序不相同,我认为数组不相等。)

Of course this can be changed if needed.

(当然,如果需要,可以更改。)

Also this code can be further enhanced to take function as argument that will be used to format diff object in arbitrary way based on passed primitive values (now this job is done by "compareValues" method).

(同样,可以进一步增强此代码以将函数用作参数,该参数将用于基于传递的原始值以任意方式格式化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); 


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...