jQuery 函数来计算两个 JavaScript 对象之间的差异

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11021893/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 11:50:38  来源:igfitidea点击:

jQuery function to compute the difference between two JavaScript objects

javascriptjqueryjsonknockout.jsunderscore.js

提问by Armchair Bronco

I have a rich AJAX-based web application that uses JQuery+ Knockout. I have a JQuery plugin that wraps my Knockout view models to expose utility methods like .reset(), .isDirty(), and so on.

我有一个使用JQuery+ Knockout的丰富的基于 AJAX 的 web 应用程序。我有一个 JQuery 插件,它包装我的 Knockout 视图模型以公开实用方法,如.reset().isDirty()等。

I have a method called .setBaseline()that essentially takes a snapshotof the data model once it has been populated (via the mapping plugin). Then I can use this snapshot to quickly determine if the model has changed.

我有一个名为.setBaseline()的方法,它在填充数据模型后(通过映射插件)基本上获取数据模型的快照。然后我可以使用这个快照来快速确定模型是否发生了变化。

What I'm looking for is some kind of general purpose functionthat can return an object that represents the differences between two 2 JavaScript objectswhere one of the objects is considered to be the master.

我正在寻找的是某种通用函数,它可以返回一个对象,该对象表示两个 2 JavaScript 对象之间的差异,其中一个对象被认为是主对象。

For example, assume that this is my snapshot:

例如,假设这是我的快照:

var snapShot = {
  name: "Joe",
  address: "123 Main Street",
  age: 30,
  favoriteColorPriority: {
     yellow: 1,
     pink: 2,
     blue: 3
  }
};

Then assume that the live data looks like this:

然后假设实时数据如下所示:

var liveData = {
    name: "Joseph",
    address: "123 Main Street",
    age: 30,
    favoriteColorPriority: {
        yellow: 1,
        pink: 3,
        blue: 2
    }
};

I want a .getChanges(snapShot, liveData)utility function that returns the following:

我想要一个返回以下内容的.getChanges(snapShot, liveData)实用程序函数:

var differences = {
    name: "Joseph",
    favoriteColorPriority: {
        pink: 3,
        blue: 2
    }
};

I was hoping that the _.underscore librarymight have something like this, but I couldn't find anything that seemed to work like this.

我希望_.underscore 库可能有这样的东西,但我找不到任何看起来像这样工作的东西。

回答by Bergi

I don't think there is such a function in underscore, but it's easy to implement yourself:

我觉得下划线没有这样的功能,但是自己实现很容易:

function getChanges(prev, now) {
    var changes = {};
    for (var prop in now) {
        if (!prev || prev[prop] !== now[prop]) {
            if (typeof now[prop] == "object") {
                var c = getChanges(prev[prop], now[prop]);
                if (! _.isEmpty(c) ) // underscore
                    changes[prop] = c;
            } else {
                changes[prop] = now[prop];
            }
        }
    }
    return changes;
}

or

或者

function getChanges(prev, now) {
    var changes = {}, prop, pc;
    for (prop in now) {
        if (!prev || prev[prop] !== now[prop]) {
            if (typeof now[prop] == "object") {
                if(c = getChanges(prev[prop], now[prop]))
                    changes[prop] = c;
            } else {
                changes[prop] = now[prop];
            }
        }
    }
    for (prop in changes)
        return changes;
    return false; // false when unchanged
}

This will not work with Arrays (or any other non-plain-Objects) or differently structured objects (removals, primitive to object type changes).

这不适用于数组(或任何其他非普通对象)或不同结构的对象(移除、原始对象类型更改)。

回答by Armchair Bronco

Posting my own answer so folks can see the final implementation that also works with arrays. In the code below, "um" is my namespace, and I'm also using the _.isArray() and _.isObject methods from Underscore.js.

发布我自己的答案,以便人们可以看到也适用于数组的最终实现。在下面的代码中,“um”是我的命名空间,我还使用了 Underscore.js 的 _.isArray() 和 _.isObject 方法。

The code that looks for "_KO" is used to skip past Knockout.js members that are present in the object.

查找“_KO”的代码用于跳过对象中存在的 Knockout.js 成员。

// This function compares 'now' to 'prev' and returns a new JavaScript object that contains only
// differences between 'now' and 'prev'. If 'prev' contains members that are missing from 'now',
// those members are *not* returned. 'now' is treated as the master list.
um.utils.getChanges = function (prev, now) {
    var changes = {};
    var prop = {};
    var c = {};
    //-----

    for (prop in now) { //ignore jslint
        if (prop.indexOf("_KO") > -1) {
            continue; //ignore jslint
        }

        if (!prev || prev[prop] !== now[prop]) {
            if (_.isArray(now[prop])) {
                changes[prop] = now[prop];
            }
            else if (_.isObject(now[prop])) {
                // Recursion alert
                c = um.utils.getChanges(prev[prop], now[prop]);
                if (!_.isEmpty(c)) {
                    changes[prop] = c;
                }
            } else {
                changes[prop] = now[prop];
            }
        }
    }

    return changes;
};

回答by srgstm

I am using JsonDiffPatchin my projects to find the delta, transfer it over the net, and then patch the object at the other end to get the exact copy. It is very easy to use and works really well.

我在我的项目中使用JsonDiffPatch来查找增量,通过网络传输它,然后在另一端修补对象以获得准确的副本。它非常易于使用并且效果很好。

And it works with arrays too!

它也适用于数组!

回答by jozop

Solution with the use of jQuery.isEmptyObject()

使用 jQuery.isEmptyObject() 解决

This reworks the getChanges(prev, now)solution above. It should work also on objects of different type and also when prev[prop]is undefined (resulting to now[prop])

这重新设计了上面的 getChanges(prev, now)解决方案。它也应该适用于不同类型的对象,并且当prev[prop]未定义时(导致现在 [prop])

function getChanges(prev, now)
{
    // sanity checks, now and prev must be an objects
    if (typeof now !== "object")
        now = {};
    if (typeof prev !== "object")
        return now;

    var changes = {};
    for (var prop in now) {
         // if prop is new in now, add it to changes
        if (!prev || !prev.hasOwnProperty(prop) ) {
            changes[prop] = now[prop];
            continue;
        }
        // if prop has another type or value (different object or literal)
        if (prev[prop] !== now[prop]) {
            if (typeof now[prop] === "object") {
                // prop is an object, do recursion
                var c = getChanges(prev[prop], now[prop]);
                if (!$.isEmptyObject(c))
                    changes[prop] = c;
            } else {
                // now[prop] has different but literal value
                changes[prop] = now[prop];
            }
        }
    }

    // returns empty object on none change
    return changes;
}

回答by B T

No need to use jquery for this. I recently wrote a module to do this: https://github.com/Tixit/odiff. Here's an example:

无需为此使用 jquery。我最近写了一个模块来做到这一点:https: //github.com/Tixit/odiff。下面是一个例子:

var a = [{a:1,b:2,c:3},              {x:1,y: 2, z:3},              {w:9,q:8,r:7}]
var b = [{a:1,b:2,c:3},{t:4,y:5,u:6},{x:1,y:'3',z:3},{t:9,y:9,u:9},{w:9,q:8,r:7}]

var diffs = odiff(a,b)

/* diffs now contains:
[{type: 'add', path:[], index: 2, vals: [{t:9,y:9,u:9}]},
 {type: 'set', path:[1,'y'], val: '3'},
 {type: 'add', path:[], index: 1, vals: [{t:4,y:5,u:6}]}
]
*/

In the readme for odiff, I listed out other modules that I determined didn't fit my needs if you want to check those out too.

在 odiff 的自述文件中,我列出了我认为不符合我需要的其他模块,如果您也想查看这些模块。

回答by Carlos

Based on the solution above. This version corrects a bug, some changes are not detected with the original solution.

基于上述解决方案。此版本更正了一个错误,原始解决方案未检测到某些更改。

getChanges = function (prev, now) {
        var changes = {}, prop, pc;
        for (prop in now) {
            if (!prev || prev[prop] !== now[prop]) {
                if (typeof now[prop] == "object") {
                    if (c = getChanges(prev[prop], now[prop]))
                        changes[prop] = c;
                } else {
                    changes[prop] = now[prop];
                }
            }
        }

        for (prop in prev) {
            if (!now || now[prop] !== prev[prop]) {
                if (typeof prev[prop] == "object") {
                    if (c = getChanges(now[prop], prev[prop]))
                        changes[prop] = c;
                } else {
                    changes[prop] = prev[prop];
                }
            }
        }

        return changes;
    };

回答by Matthias

Be aware that typeof now[prop] == "object"is even true if now[prop]is a Date.

请注意,typeof now[prop] == "object"即使now[prop]是日期也是如此。

Instead of: typeof now[prop] == "object"

代替: typeof now[prop] == "object"

I used: Object.prototype.toString.call( now[prop] === "[object Object]")

我用了: Object.prototype.toString.call( now[prop] === "[object Object]")