【问题标题】:Exclude some properties in comparison using isEqual() of lodash比较使用 lodash 的 isEqual() 排除一些属性
【发布时间】:2016-06-20 12:46:17
【问题描述】:

我正在使用 _.isEqual 比较 2 个对象数组(例如:每个对象 10 个属性),它工作正常。

现在有 2 个属性(创建和删除)我不需要参与比较。

示例:

var obj1 = {name: "James", age: 17, creation: "13-02-2016", deletion: "13-04-2016"}
var obj2 = {name: "Maria", age: 17, creation: "13-02-2016", deletion: "13-04-2016"}

// lodash method...
_.isEqual(firstArray, secondArray)

【问题讨论】:

    标签: javascript lodash


    【解决方案1】:

    您可以使用omit() 删除对象中的特定属性。

    var result = _.isEqual(
      _.omit(obj1, ['creation', 'deletion']),
      _.omit(obj2, ['creation', 'deletion'])
    );
    

    var obj1 = {
      name: "James",
      age: 17,
      creation: "13-02-2016",
      deletion: "13-04-2016"
    };
    
    var obj2 = {
      name: "Maria",
      age: 17,
      creation: "13-02-2016",
      deletion: "13-04-2016"
    };
    
    var result = _.isEqual(
      _.omit(obj1, ['creation', 'deletion']),
      _.omit(obj2, ['creation', 'deletion'])
    );
    
    console.log(result);
    <script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>

    【讨论】:

    • 最后一个问题,如果不是一个对象(例如:obj1)是一个对象数组,我该怎么办?
    • 我不明白你的问题,举个例子。
    • 例如,我有一个对象数组,而不是 1 个对象,我希望在其中省略属性创建和删除,因为我注意到 omit 函数仅支持对象而不支持对象数组
    • 这将创建每个对象的副本。我建议改用isEqualWith。这也适用于任何形式的嵌套对象/数组。
    • 您是否也可以使用它来排除名称中带有通配符的所有属性,例如:_.omit(obj, ['creat*']),因此它将排除 create、created、creation..?
    【解决方案2】:

    @ryeballar 的回答不适用于大型对象,因为您每次进行比较时都会创建每个对象的深层副本。

    最好使用isEqualWith。例如,忽略“创建”和“删除”属性的差异:

    var result = _.isEqualWith(obj1, obj2, (value1, value2, key) => {
        return key === "creation" || key === "deletion" ? true : undefined;
    });
    

    编辑(cmets 中指出的重要警告):如果对象具有不同数量的键,则isEqualWith 认为它们是不同的,无论您的定制器做什么。因此如果您想忽略可选属性,请不要使用此方法。请考虑使用_.isMatch()_.isMatchWith() 或@ryeballar 的_.omit() 方法。

    请注意,如果您正在为 ES5 及更早版本编写代码,则必须将箭头语法 (() => {) 替换为函数语法 (function() {)

    【讨论】:

    • 看来isEqualWith没有按预期工作 (see this issue)
    • 根据 lodash 人的说法,“问题”是预期的行为。它工作正常,请记住,对于第一次迭代,value1value2 将是您的原始对象,而 key 将是未定义的。只要在这种情况下返回undefined(我的示例就是这样做的),那么isEqualWith 就会递归到对象的属性中。
    • 在 Lodash 4.17.11 上,只有当两个对象具有相同数量的键时,它才会起作用。否则,isEqualWith 将预先检查两个对象是否具有相同数量的键,并在进入下一个遍历每个键的循环之前返回 false。因此,如果一个对象具有创建键而另一个对象没有,它实际上不会忽略该字段。
    • @maxcnunes 如何解决这种不便,而不必像上一个答案中建议的那样深度克隆对象?
    • 我编辑了我的答案 - @Luke 你可以试试_.isMatch(),因为它会忽略第一个对象中缺少的所有属性。
    【解决方案3】:

    _.omit 创建对象的深拷贝。如果您只需要排除根 props,最好创建 浅拷贝,例如使用 解构赋值

    const x = { a: 4, b: [1, 2], c: 'foo' }
    const y = { a: 4, b: [1, 2], c: 'bar' }
    
    const { c: xC, ...xWithoutC } = x
    const { c: yC, ...yWithoutC } = y
    
    _.isEqual(xWithoutC, yWithoutC) // true
    xWithoutC.b === x.b             // true, would be false if you use _.omit
    

    最好的方法是根本不创建副本(TypeScript):

    function deepEqual(
      x?: object | null,
      y?: object | null,
      ignoreRootProps?: Set<string>
    ) {
      if (x == null || y == null) return x === y
    
      const keys = Object.keys(x)
      if (!_.isEqual(keys, Object.keys(y)) return false
    
      for (let key of keys) {
        if (ignoreRootProps && ignoreRootProps.has(key)) continue
        if (!_.isEqual(x[key], y[key])) return false
      }
      return true
    }
    

    【讨论】:

      【解决方案4】:

      您可以将您的数组map 转换为“清理过的”数组,然后进行比较。

      // Create a function, to do some cleaning of the objects.
      var clean = function(obj) {
          return {name: obj.name, age: obj.age};
      };
      
      // Create two new arrays, which are mapped, 'cleaned' copies of the original arrays.
      var array1 = firstArray.map(clean);
      var array2 = secondArray.map(clean);
      
      // Compare the new arrays.
      _.isEqual(array1, array2);
      

      如果对象需要任何新属性,则需要更新 clean 函数。可以对其进行编辑,以便删除两个不需要的属性。

      【讨论】:

        【解决方案5】:

        我看到了两个选项。

        1) 为每个不包含创建或日期的对象制作第二个副本。

        2) 遍历所有属性,假设您确定它们都具有相同的属性,请尝试这样的操作。

        var x ={}
        var y ={}
        for (var property in x) {
        if(property!="creation" || property!="deletion"){
        if (x.hasOwnProperty(property)) {
                compare(x[property], y[property])
            }
        }
        }
        

        其中 compare() 是一些简单的字符串或对象比较。如果您确定一个或两个对象的属性,您可以进一步简化此代码,但这应该适用于大多数情况。

        【讨论】:

          【解决方案6】:

          我的最终解决方案需要忽略可选属性的完整比较,因此上述解决方案不起作用。

          在与isEqual 进行比较之前,我使用浅克隆从每个对象中删除了我想忽略的键:

          const equalIgnoring = (newItems, originalItems) => newItems.length === originalItems.length
              && newItems.every((newItem, index) => {
                  const rest1 = { ...newItem };
                  delete rest1.creation;
                  delete rest1.deletion;
                  const rest2 = { ...originalItems[index] };
                  delete rest2.creation;
                  delete rest2.deletion;
                  return isEqual(rest1, rest2);
              });
          

          如果您想检查数组中每个项目的子集,这可行:

          const equalIgnoringExtraKeys = (fullObjs, partialObjs) => 
              fullObjs.length === partialObjs.length
              && fullObjs.every((fullObj, index) => isMatch(fullObj, partialObjs[index]));
          

          如果您还想忽略特定属性并检查子集:

          const subsetIgnoringKeys = (fullObjs, partialObjs) => 
              fullObjs.length === partialObjs.length
              && fullObjs.every((fullObj, index) => isMatchWith(
                  fullObj,
                  partialObjs[index],
                  (objValue, srcValue, key, object, source) => {
                      if (["creation", "deletion"].includes(key)) {
                          return true;
                      }
                      return undefined;
                  }
              ));
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-01-27
            • 1970-01-01
            • 1970-01-01
            • 2022-11-02
            • 1970-01-01
            • 2018-10-12
            • 2017-03-12
            • 2018-05-27
            相关资源
            最近更新 更多