比较JavaScript中的对象数组

时间:2020-03-05 18:43:04  来源:igfitidea点击:

我想比较JavaScript代码中2个对象数组。这些对象共有8个属性,但是每个对象都不会有一个值,并且数组永远不会大于8个项目,因此可能需要遍历每个对象然后查看对象的值的蛮力方法。 8个属性是执行我想做的最简单的方法,但是在实现之前,我想看看是否有人有一个更优雅的解决方案。有什么想法吗?

解决方案

回答

编辑:我们不能在JavaScript解释器的当前基于浏览器的常见实现中重载运算符。

为了回答最初的问题,我们可以采取一种方式来解决这个问题,请注意,这有点麻烦,只需将两个数组序列化为JSON,然后比较两个JSON字符串即可。这只会告诉我们数组是否不同,显然我们可以对数组中的每个对象执行此操作,以查看哪些对象不同。

另一个选择是使用一个具有一些不错的工具的库来比较我使用的对象并推荐MochiKit。

编辑:kamens给出的答案也值得考虑,因为一个用于比较两个给定对象的函数比任何执行我建议的库要小得多(尽管我的建议肯定足够好)。

这是一个简单的实现,可能足以使我们意识到此实现存在潜在的问题:

function objectsAreSame(x, y) {
   var objectsAreSame = true;
   for(var propertyName in x) {
      if(x[propertyName] !== y[propertyName]) {
         objectsAreSame = false;
         break;
      }
   }
   return objectsAreSame;
}

假定两个对象都具有相同的确切属性列表。

哦,很明显,无论好坏,我都属于唯一的一个返回点阵营。 :)

回答

老实说,最多有8个对象,每个对象最多有8个属性,最好的选择是只遍历每个对象并直接进行比较。它会很快并且会很容易。

如果我们将经常使用这些类型的比较,那么我同意Jason关于JSON序列化的信息...但是否则,无需使用新的库或者JSON序列化代码来降低应用程序的速度。

回答

@JasonBunting的答案中提到的objectsAreSame函数对我来说很好用。但是,有一个小问题:如果x [propertyName]y [propertyName]是对象(typeof x [propertyName] =='object'),则需要递归调用该函数才能比较它们。

回答

我已经做了一些简单的算法来比较两个对象的内容并返回可理解的差异列表。以为我会分享。它借鉴了jQuery的一些思想,即map函数的实现以及对象和数组类型的检查。

它返回"差异对象"列表,该列表是具有差异信息的数组。非常简单。

这里是:

// compare contents of two objects and return a list of differences
// returns an array where each element is also an array in the form:
// [accessor, diffType, leftValue, rightValue ]
//
// diffType is one of the following:
//   value: when primitive values at that index are different
//   undefined: when values in that index exist in one object but don't in 
//              another; one of the values is always undefined
//   null: when a value in that index is null or undefined; values are
//         expressed as boolean values, indicated wheter they were nulls
//   type: when values in that index are of different types; values are 
//         expressed as types
//   length: when arrays in that index are of different length; values are
//           the lengths of the arrays
//

function DiffObjects(o1, o2) {
    // choose a map() impl.
    // you may use $.map from jQuery if you wish
    var map = Array.prototype.map?
        function(a) { return Array.prototype.map.apply(a, Array.prototype.slice.call(arguments, 1)); } :
        function(a, f) { 
            var ret = new Array(a.length), value;
            for ( var i = 0, length = a.length; i < length; i++ ) 
                ret[i] = f(a[i], i);
            return ret.concat();
        };

    // shorthand for push impl.
    var push = Array.prototype.push;

    // check for null/undefined values
    if ((o1 == null) || (o2 == null)) {
        if (o1 != o2)
            return [["", "null", o1!=null, o2!=null]];

        return undefined; // both null
    }
    // compare types
    if ((o1.constructor != o2.constructor) ||
        (typeof o1 != typeof o2)) {
        return [["", "type", Object.prototype.toString.call(o1), Object.prototype.toString.call(o2) ]]; // different type

    }

    // compare arrays
    if (Object.prototype.toString.call(o1) == "[object Array]") {
        if (o1.length != o2.length) { 
            return [["", "length", o1.length, o2.length]]; // different length
        }
        var diff =[];
        for (var i=0; i<o1.length; i++) {
            // per element nested diff
            var innerDiff = DiffObjects(o1[i], o2[i]);
            if (innerDiff) { // o1[i] != o2[i]
                // merge diff array into parent's while including parent object name ([i])
                push.apply(diff, map(innerDiff, function(o, j) { o[0]="[" + i + "]" + o[0]; return o; }));
            }
        }
        // if any differences were found, return them
        if (diff.length)
            return diff;
        // return nothing if arrays equal
        return undefined;
    }

    // compare object trees
    if (Object.prototype.toString.call(o1) == "[object Object]") {
        var diff =[];
        // check all props in o1
        for (var prop in o1) {
            // the double check in o1 is because in V8 objects remember keys set to undefined 
            if ((typeof o2[prop] == "undefined") && (typeof o1[prop] != "undefined")) {
                // prop exists in o1 but not in o2
                diff.push(["[" + prop + "]", "undefined", o1[prop], undefined]); // prop exists in o1 but not in o2

            }
            else {
                // per element nested diff
                var innerDiff = DiffObjects(o1[prop], o2[prop]);
                if (innerDiff) { // o1[prop] != o2[prop]
                    // merge diff array into parent's while including parent object name ([prop])
                    push.apply(diff, map(innerDiff, function(o, j) { o[0]="[" + prop + "]" + o[0]; return o; }));
                }

            }
        }
        for (var prop in o2) {
            // the double check in o2 is because in V8 objects remember keys set to undefined 
            if ((typeof o1[prop] == "undefined") && (typeof o2[prop] != "undefined")) {
                // prop exists in o2 but not in o1
                diff.push(["[" + prop + "]", "undefined", undefined, o2[prop]]); // prop exists in o2 but not in o1

            }
        }
        // if any differences were found, return them
        if (diff.length)
            return diff;
        // return nothing if objects equal
        return undefined;
    }
    // if same type and not null or objects or arrays
    // perform primitive value comparison
    if (o1 != o2)
        return [["", "value", o1, o2]];

    // return nothing if values are equal
    return undefined;
}