获取两个javascript对象的增量 [英] Get the delta of two javascript objects
问题描述
我有两个大的嵌套javascript对象,我想比较它们并创建一个只代表差异的对象。我打算用它来创建一个PATCH请求。
给定 oldObj
和 newObj
:
- 仅在
newObj
上的属性应该是在diff - 仅在
oldObj
上的属性应该在diff - 属性中两个对象上的值应该使用
newObj
中的值,如果值是数组,字符串或数字 - 对象应该是递归比较
- 无需合并数组,完全替换即可。
这可能看起来像重复一样,但我认为不是。 此解决方案(1)仅限一级(下面的答案是非递归的,打击在阵列上,并不是双向的)。 此解决方案(2)返回未更改的属性不是目标输入/输出:
diff( {A:1},{A:0}); // {a:0}
diff({a:1},{b:1}); // {a:1,b:1}
diff({
a:{x:1},
b:1
},
{
a:{x:0},
b:1
})// {a:{x:0}}
diff({a:[1, 3,5,7]},{一个:[1,3,7]}); // {a:[1,3,7]}
我使用的是以下方法从解决方案1修改它符合所有标准,除了 diff({a:1},{b:1})// {a:1,b:1}
因为它只在一个方向上进行比较。
jsonDiff = function(oldObject,newObject){
var diff,i,innerDiff;
diff = {};
for(i in newObject){
innerDiff = {};
if(_.isArray(newObject [i])){
if(!_。isEqual(newObject [i],oldObject [i])){
diff [i] = newObject [一世];
}
}否则if(typeof newObject [i] ==='object'){
innerDiff = jsonDiff(oldObject [i],newObject [i]);
if(!_。isEmpty(innerDiff)){
diff [i] = innerDiff;
}
}否则if(!oldObject){
diff [i] = newObject [i];
} else if(!oldObject.hasOwnProperty(i)){
diff [i] = newObject [i];
} else if(oldObject [i]!== newObject [i]){
diff [i] = newObject [i];
}
}
返回差异;
};
我见过 jsonDiffPatch 库,但我不需要它创建的所有元数据,只需要原始diff对象。这是一个迷你图书馆吗?看起来很有必要实现 PATCH
很好,但我找不到一个。任何人都有一个小小的要点吗?
这是一个应该适合你的功能,比代码更多的评论:
// diffObjs:返回JavaScript值之间的差异
//
//功能:
/ /
//比较两个JavaScript值,并返回一个两元素
//数组,其中包含两者之间差异
//的最小表示。
//
//值可以是标量(例如,字符串,整数,布尔值)或对象,
//包括数组。当两个值完全匹配时,即
//如果两者之间的'==='运算符返回'true',则返回NULL。
//
//当结果包含一个对象或数组时,只从参数中复制数据,而不是引用
//。这使得大小的结果
//但其操作不会影响原始参数。
//
// Args:
// v1,v2:要比较的值
//
//特定行为:
//
// *如果v1 === v2 *
//
//返回NULL当两个标量(非对象)值匹配时,或者当相同的
//对象时,会发生这种情况或者在两个参数中传递数组。
//例如,
//
// var my_obj = {member1:0,member1:'dog'};
// var my_array = [1,'cat'];
// var my_int = 7;
// var no_val = null;
//
// diffObjs(my_int,my_int)==> NULL
// diffObjs(1,1)==> NULL
// diffObjs(my_obj,my_obj)==> NULL
// diffObjs({x:1,y:2},{x:1,y:2})==> NULL
// diffObjs(my_array,my_array)==> NULL
// diffObjs([1,'a'],[1,'1'])==> NULL
// diffObjs(null,null)==> NULL
// diffObjs(no_val,null)==> NULL
//
// *在类型不匹配时返回v1和v2的副本*:
//
//当v1和v2的类型不同或者一个是数组时而另一个
//是一个对象,结果数组将包含
// v1和v2的exect副本。
//
// *返回非数组对象之间差异的最小表示*:
//
//否则,当传入两个对象时,元素0 $ b结果数组中的$ b //包含v1但不存在v2的成员及其值
//,或者
// v1和v2中存在的具有不同值的成员。元素1包含
//相同但相对于v2,即v2但不存在v1的成员及其
//值,或者
中存在的成员//两者都是v1和v2具有不同的值。
//
//注意:仅当
//它们特定于相应值参数
//的对象时或者当成员存在于两者中并具有不同的值。因此,
//调用者可以判断对象不匹配是否存在
//因为成员对一个对象的特异性与不匹配
//的值,其中一个为null,另一个为null不是。
//
//示例:
// diffObjs({a:10,b:dog},{a:1,b:dog} ==> [ {a:10},{a:1}]
// diffObjs({a:10},{a:10,b:dog} ==> [{},{b:dog }]
// diffObjs({a:10,c:null},{a:10,b:dog} ==> [{c:null},{b:dog} ]
// diffObjs({a:[1],b:cat},{a:1,b:dog} ==> [{a:[1],b:cat },{a:1,b:dog}]
// diffObjs(
// {a:{m1:x,m2:y},b:3} ,
// {a:{m1:x,m2:z,m3:1},b:3})==> [{a:{m2:y}}, {a:{m2:z,m3:1}}]
//
// *当按位置或值不同时,返回比较数组的副本*
//
//如果两个参数数组,元素0和1
//中的结果将包含数组形式的结果,这些结果与
//的值和顺序都不匹配。如果两个位置对应
//数组参数中的元素具有相同的值(例如,两个
//具有匹配值的标量或对同一个对象的两个引用),
//数组中的相应值将为null。结果数组中数组的
//基数因此
//总是匹配相应参数的基数。
//
//示例:
// diffObjs([1,2],[1,2])==> [[null,null],[null,null]]
// diffObjs([1,2],[2,1])==> [[1,2],[2,1]]
// diffObjs([1,2],[1,2,3])==> [[1,2,null],[2,1,3]]
// diffObjs([1,1,2,3],[1,2,3])==> [[null,1,2,3],[null,2,3]]
//
var diffObjs = function(v1,v2){
//传递对
的引用时返回NULL //相同的对象或匹配的标量值
if(v1 === v2){
返回null;
}
var cloneIt = function(v){
if(v == null || typeof v!='object'){
return v;
}
var isArray = Array.isArray(v);
var obj = isArray? []:{};
if(!isArray){
//处理函数等
Object.assign({},v);
}
for(var i in v){
obj [i] = cloneIt(v [i]);
}
return obj;
}
//与非数组
相比的不同类型或数组if(typeof v1!= typeof v2 || Array.isArray(v1)!= Array.isArray( v2)){
return [cloneIt(v1),cloneIt(v2)];
}
//不同的标量(不需要克隆)
if(typeof v1!='object'&& v1!== v2){
return [v1,v2];
}
//一个是空的,另一个不是
//(如果它们都是空的,'==='比较
//上面不允许我们这里)
if(v1 == null || v2 == null){
return [cloneIt(v1),cloneIt(v2)];
}
//我们有两个对象或两个要比较的数组。
var isArray = Array.isArray(v1);
var left = isArray? []:{};
var right = isArray? []:{};
for(var i in v1){
if(!v2.hasOwnProperty(i)){
left [i] = cloneIt(v1 [i]);
} else {
var sub_diff = diffObjs(v1 [i],v2 [i]);
//将
//两个对象之间的差异复制到结果中。
// - 如果对象是数组,使用'null'
//来表示两个对应的元素
//匹配。
//
// - 如果对象不是数组,则仅复制
//指向不匹配的
//对象的成员。
if(isArray || sub_diff){
left [i] = sub_diff? cloneIt(sub_diff [0]):null;
right [i] = sub_diff? cloneIt(sub_diff [1]):null;
}
}
}
for(var i in v2){
if(!v1.hasOwnProperty(i)){
right [i] = cloneIt(v2 [i]);
}
}
返回[left,right];
};
I have two large, nested javascript objects and I would like to compare them and create an object that represents only the differences. I intend to use this to create a PATCH request.
Given oldObj
and newObj
:
- Properties that are only on
newObj
should be in the diff - Properties that are only on
oldObj
should be in the diff - Properties that are on both objects should use the value from
newObj
if the value is an array, string, or number - The objects should be recursively compared
- No need to merge arrays fancily, complete replace is ok
This may look like a duplicate, but I don't think it is. This solution (1) is one level deep only (the answer below is non-recursive, blows up on arrays, and is not bi-directional). this solution (2) returns unchanged properties is not bi-directional.
Target input/output:
diff({a:1},{a:0}); // {a:0}
diff({a:1},{b:1}); // {a:1,b:1}
diff({
a: { x: 1 },
b: 1
},
{
a: { x: 0 },
b: 1
}) // {a:{x:0}}
diff({a:[1,3,5,7]},{a:[1,3,7]}); // {a:[1,3,7]}
I am using the following method which is modified from solution 1. It meets all criteria except diff({a:1},{b:1}) // {a:1,b:1}
because it only compares in one direction.
jsonDiff = function(oldObject, newObject) {
var diff, i, innerDiff;
diff = {};
for (i in newObject) {
innerDiff = {};
if (_.isArray(newObject[i])) {
if (!_.isEqual(newObject[i], oldObject[i])) {
diff[i] = newObject[i];
}
} else if (typeof newObject[i] === 'object') {
innerDiff = jsonDiff(oldObject[i], newObject[i]);
if (!_.isEmpty(innerDiff)) {
diff[i] = innerDiff;
}
} else if (!oldObject) {
diff[i] = newObject[i];
} else if (!oldObject.hasOwnProperty(i)) {
diff[i] = newObject[i];
} else if (oldObject[i] !== newObject[i]) {
diff[i] = newObject[i];
}
}
return diff;
};
I have seen the jsonDiffPatch library, but I do not need all the metadata it creates, just the raw diff object. Is there a mini library that just does this? Seems sort of necessary to implement PATCH
nicely, but I can't find one. Anyone have a small gist for this?
Here is a function that should work for you, more comments than code:
// diffObjs: return differences between JavaScript values
//
// Function:
//
// Compare two JavaScript values, and return a two-element
// array that contains a minimal representation of the difference
// between the two.
//
// Values may be scalar (e.g., string, integer, boolean) or objects,
// including arrays. When the two values match exactly, that is,
// if the '===' operator between the two would return 'true', we return NULL.
//
// When the result contains an object or array, only data, not references,
// are copied from the arguments. This makes for a large size result
// but one whose manipulation will not affect the original arguments.
//
// Args:
// v1, v2: values to compare
//
// Specific behaviors:
//
// *Return NULL if v1 === v2*
//
// This happens when two scalar (non-object) values match, or when the same
// object or array is passed in both arguments.
// e.g.,
//
// var my_obj = { member1: 0, member1: 'dog' };
// var my_array = [ 1, 'cat' ];
// var my_int = 7;
// var no_val = null;
//
// diffObjs(my_int, my_int) ==> NULL
// diffObjs(1, 1) ==> NULL
// diffObjs(my_obj, my_obj) ==> NULL
// diffObjs({x:1,y:2}, {x:1,y:2}) ==> NULL
// diffObjs(my_array, my_array) ==> NULL
// diffObjs([1,'a'], [1,'1']) ==> NULL
// diffObjs(null, null) ==> NULL
// diffObjs(no_val, null) ==> NULL
//
// *Return copies of v1 and v2 on type mismatch*:
//
// When type of v1 and v2 are different or one is an array and the other
// is an object, the result array will contain exect copies of both
// v1 and v2.
//
// *Return minimal representation of differences among non-array objects*:
//
// Otherwise, when two objects are passed in, element 0
// in the result array contains the members and their values
// that exist in v1 but not v2, or members that exist in both
// v1 and v2 that have different values. Element 1 contains
// the same but with respect to v2, that is members and their
// values that exist in v2 but not v1, or members that exist in
// both v1 and v2 that have different values.
//
// Note: The members are represented in the result objects only when
// they are specific to the object of the corresponding value argument
// or when the members exist in both and have different values. The
// caller therefore can tell whether the object mismatch exists
// because of specificity of a member to one object vs. a mismatch
// in values where one is null and the other is not.
//
// Examples:
// diffObjs({a:10, b:"dog"}, {a:1, b:"dog"} ==> [ {a:10}, {a:1} ]
// diffObjs({a:10}, {a:10, b:"dog"} ==> [ {}, {b:"dog"} ]
// diffObjs({a:10, c:null}, {a:10, b:"dog"} ==> [ {c:null}, {b:"dog"} ]
// diffObjs({a:[1], b:"cat"},{a:1, b:"dog"} ==> [ {a:[1], b:"cat"}, {a:1, b:"dog"} ]
// diffObjs(
// {a:{ m1:"x", m2:"y"}, b:3 },
// {a:{ m1:"x", m2:"z", m3:1 }, b:3 } ) ==> [ {a:{m2:"y"}}, {a:{m2:"z",m3:1}} ]
//
// *Return copies of compared arrays when differing by position or value*
//
// If the two arguments arrays, the results in elements 0 and 1
// will contain results in array form that do not match with respect
// to both value and order. If two positionally corresponding
// elements in the array arguments have identical value (e.g., two
// scalars with matching values or two references to the same object),
// the corresponding values in the array will be null. The
// cardinality of the arrays within the result array will therefore
// always match that of the corresponding arguments.
//
// Examples:
// diffObjs([1,2], [1,2]) ==> [ [null,null], [null,null] ]
// diffObjs([1,2], [2,1]) ==> [ [1,2], [2,1] ]
// diffObjs([1,2], [1,2,3]) ==> [ [1,2,null], [2,1,3] ]
// diffObjs([1,1,2,3], [1,2,3]) ==> [ [null,1,2,3], [null,2,3] ]
//
var diffObjs = function(v1, v2) {
// return NULL when passed references to
// the same objects or matching scalar values
if (v1 === v2) {
return null;
}
var cloneIt = function(v) {
if (v == null || typeof v != 'object') {
return v;
}
var isArray = Array.isArray(v);
var obj = isArray ? [] : {};
if (!isArray) {
// handles function, etc
Object.assign({}, v);
}
for (var i in v) {
obj[i] = cloneIt(v[i]);
}
return obj;
}
// different types or array compared to non-array
if (typeof v1 != typeof v2 || Array.isArray(v1) != Array.isArray(v2)) {
return [cloneIt(v1), cloneIt(v2)];
}
// different scalars (no cloning needed)
if (typeof v1 != 'object' && v1 !== v2) {
return [v1, v2];
}
// one is null, the other isn't
// (if they were both null, the '===' comparison
// above would not have allowed us here)
if (v1 == null || v2 == null) {
return [cloneIt(v1), cloneIt(v2)];
}
// We have two objects or two arrays to compare.
var isArray = Array.isArray(v1);
var left = isArray ? [] : {};
var right = isArray ? [] : {};
for (var i in v1) {
if (!v2.hasOwnProperty(i)) {
left[i] = cloneIt(v1[i]);
} else {
var sub_diff = diffObjs(v1[i], v2[i]);
// copy the differences between the
// two objects into the results.
// - If the object is array, use 'null'
// to indicate the two corresponding elements
// match.
//
// - If the object is not an array, copy only
// the members that point to an unmatched
// object.
if (isArray || sub_diff) {
left[i] = sub_diff ? cloneIt(sub_diff[0]) : null;
right[i] = sub_diff ? cloneIt(sub_diff[1]) : null;
}
}
}
for (var i in v2) {
if (!v1.hasOwnProperty(i)) {
right[i] = cloneIt(v2[i]);
}
}
return [ left, right];
};
这篇关于获取两个javascript对象的增量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!