获取两个javascript对象的增量 [英] Get the delta of two javascript objects

查看:82
本文介绍了获取两个javascript对象的增量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个大的嵌套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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆