使用深度比较还是json.stringify来比较两个对象?

4

我有一个复杂的JSON对象,我想进行以下比较:

$scope.new = [
  {
    "name": "Node-1",
    "isParent": true,
    "text" : [
       {
           "str" : "This is my first Node-1 string",
           "parent":[]
       },
       {
          "str" : "This is my second Node-1 string",
           "parent":[]
       }],
     "nodes": [
      {
        "name": "Node-1-1",
        "isParent": false,
         "text" : [
           {
             "str" : "This is my first Node-1-1 string",
             "parent":[]
           },
           {
             "str" : "This is my second Node-1-1 string",
             "parent":[]
           }],
           "nodes": [
           {
            "name": "Node-1-1-1",
            "isParent": false,
            "text" : [
            {
              "str" : "This is my first Node-1-1-1 string",
              "parent":[]
            },
            {
              "str" : "This is my second Node-1-1-1 string",
              "parent":[]
            }],
            "nodes": []
          }
        ]
      }
    ]
  }
]

但是,当我进行比较时,我想要忽略一个属性,但是由于我正在使用Angular.js,我没有看到任何选项可以在比较两个对象时省略该属性。

 console.log(angular.equals($scope.new,$scope.copy)); 

在研究过程中,我找到了以下使用lodash的答案,它具有emit选项,但问题是我猜测omit会创建一个副本,在lodash的情况下可能会导致性能下降。

使用lodash的isEqual()排除某些属性进行比较

因此,现在我想将对象转换为字符串,然后进行比较,我想这样会更快,但问题是在字符串比较时如何省略该属性?

像这样:

var str1 = JSON.stringify(JSON.stringify($scope.new));

var str2 = JSON.stringify(JSON.stringify($scope.copy));

console.log(str1==str2);

注意:我想在比较两个对象时忽略isParent属性。

最好的方法是什么来比较两个对象?


1
你是想在比较时只忽略顶层对象的 isParent 属性,还是所有嵌套对象的 isParent 属性都要忽略? - Guillermo Moratorio
@GuillermoMoratorio:当比较所有嵌套对象时,我想忽略isParent。 - Learning-Overthinker-Confused
2个回答

2

在这些情况下,将对象保留为对象而不是转换为字符串并不是最佳方法。

使用loadash:

const propertiesToExclude = ['isParent'];
let result = _.isEqual(
  _.omit(obj1, propertiesToExclude),
  _.omit(obj2, propertiesToExclude)
);

使用纯AngularJS,创建一个去除不需要属性并进行比较的对象副本:
let firstObj = angular.copy(obj1);
let secondObj = angular.copy(obj2);
const propertiesToExclude = ['isParent'];
function removeNotComparatedProperties(obj) {
  propertiesToExclude.forEach(prop => {
    delete obj[prop];
  });
}

removeNotComparatedProperties(firstObj);
removeNotComparatedProperties(secondObj);
angular.equals(firstObj, secondObj);

为您的帮助之举点赞,但是您不觉得在 Angular 的情况下,我们增加了两个开销吗?一个是循环和删除属性,然后进行比较。哪种解决方案更快:Lodash 还是 Angular 方法? - Learning-Overthinker-Confused
让我们先说一下,对于你正在使用的对象来说,谈论性能是相当无用的。同样适用于数组长度和要排除比较的属性。顺便问一句,你认为loadash在内部做了什么?魔法吗?它们循环遍历对象的属性,并将其与您传递的属性进行检查。 而_.omit则会创建对象本身的副本(就像angular.copy一样)。 我想性能应该差不多,但如果你想要一个精确的答案,请使用jsperf,设置你的代码并测试这两种情况的性能:https://jsperf.com/ - quirimmo
如果是我的情况,我会使用什么?这取决于情况。如果我已经在其他地方使用或计划使用loadash,我会选择loadash解决方案,因为它更紧凑和更短。如果我只是为了这个目的而包含loadash,我会选择第二个选项。 - quirimmo

1

如果您使用_.isEqualWith,则可以使用lodash并覆盖用于深度比较的标准比较器:

var objA = {
  isParent: true,
  foo: {
    isParent: false,
    bar: "foobar"
  }
};

var objB = {
  isParent: false,
  foo: {
    isParent: true,
    bar: "foobar"
  }
};

var comparator = function(left, right, key) {
  if (key === 'isParent') return true; // if the key is 'isParent', mark the values equal
  else return undefined;               // else fall back to the default behavior
}

var isEqual = _.isEqualWith(objA, objB, comparator);

console.log(isEqual); // true
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>


为了排除多个属性,相应地扩展比较函数:
var comparator = function(left, right, key) {
  if (key === 'isParent' || key === 'anotherKey') return true;
  else return undefined;
}

你还可以采用不同的语法方式,根据个人喜好选择 -- switch语句、迭代数组等等。

1
感谢您对我的帮助的支持,但是您认为使用lodash进行深度比较比字符串比较更快吗? - Learning-Overthinker-Confused
性能取决于实现并且需要经过测试。我非常确定它不会比字符串比较慢,因为序列化本身就不是特别快。但撇开这个不谈,你还需要处理在字符串比较时忽略isParent属性的情况,所以实际上并没有节省任何东西。 - TimoStaudinger
只需相应地扩展比较器即可。 - TimoStaudinger
好的,如果我想省略属性如prop1、prop2,那么我就必须为prop1和prop2添加两个额外的条件,就像你为isParent添加的一样? - Learning-Overthinker-Confused
我尝试扩展比较器以适用于多个属性,但它没有起作用。您能否请展示一下如何为多个属性扩展多个比较器? - Learning-Overthinker-Confused
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接