【问题标题】:using underscore's “difference” method on arrays of objects在对象数组上使用下划线的“差异”方法
【发布时间】:2012-10-20 06:45:26
【问题描述】:
_.difference([], [])

当我拥有像

这样的原始类型数据时,此方法可以正常工作
var a = [1,2,3,4];
var b = [2,5,6];

_.difference(a,b) 调用返回[1,3,4]

但如果我使用像

这样的对象
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

好像没用

【问题讨论】:

  • stackoverflow.com/questions/8672383/…相似(不重复)
  • 如果您来这里寻找两个对象之间的差异(即它们的增量,而不是对象数组之间的差异),Underscore 没有内置任何内容,但您可以尝试stackoverflow.com/a/25651677/1037948
  • 请注意 _difference(a,b) 将返回 [1,3,4]。 _.difference 方法将返回第一个数组中不存在于其他数组中的元素

标签: javascript underscore.js


【解决方案1】:

原因很简单,内容相同的对象不是相同的对象,例如

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}]; 
a.indexOf({'id':1, 'value':10})

它不会返回 0 而是 -1,因为我们正在搜索不同的对象

查看源码http://underscorejs.org/underscore.js_.difference使用_.contains

_.difference = function(array) {
  var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
  return _.filter(array, function(value){ return !_.contains(rest, value); });
};

_.contains 最终使用indexOf,因此除非它们指向同一个对象,否则不会找到对象。

您可以通过循环所有项目并调用比较回调来改进下划线_.contains,您应该能够将其传递给差异或包含函数,或者您可以检查this version which improves contains methods

【讨论】:

【解决方案2】:

试试这个大小来找出对象数组的差异:

var test = [{a: 1},{b: 2}];
var test2 = [{a: 1}];

_.filter(test, function(obj){ return !_.findWhere(test2, obj); });

【讨论】:

  • 你成功了。优雅。
  • 可以(几乎?)重写为 _.reject(test, _.matches(test2[0])); 如果您只是针对单个对象进行比较
  • 如果您想按特定 id 过滤,请使用:_.filter(test, function(obj){ return !_.findWhere(test2, {id: obj.id}); });
【解决方案3】:

虽然接受的答案是正确的,并且其他答案也给出了很好的想法,但还有一个非常容易使用下划线实现的附加选项。

此解决方案依赖于每个对象都有一个唯一的 ID,但在许多情况下确实如此,您只需两行代码即可获得两个对象数组的差异。

使用 underscore 的“pluck”方法,您可以快速构建一个包含源集中和目标集中所有 ID 的数组。从那里开始,所有下划线的数组方法都将起作用,差异、联合、交集等......

操作后,从源列表中获取您想要的对象列表是很简单的。这是一个例子:

详细:

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

var arr1 = _.pluck(a, "id");
var arr2 = _.pluck(b, "id");
var diff = _.difference(arr1, arr2);
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });

或者,更简洁:

var diff = _.difference(_.pluck(a, "id"), _.pluck(b, "id"));
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });

当然,同样的技术可以扩展用于任何数组方法。

【讨论】:

  • 很好的答案,我喜欢 kontr0l 但 Angular 喜欢向对象添加 hashkey 属性,因此如果您要比较的对象还没有 hashkey,那么比较整个对象就变得很困难。有了你的回答,我可以单独比较 ID,效果很好。
  • 警告 pluck 不存在,阅读此changelog lodash 4.0.0 现在您可以使用 _.map
  • @maliness,澄清一下,对于 lodash 不存在 pluck,但它 确实 仍然存在于下划线中,这就是问题所在参考。
【解决方案4】:

请原谅我来晚了,但这可能会有所帮助:

array_of_objects = 
    // return the non-matching items (without the expected properties)
    _.difference(array_of_objects,
        // filter original list for items with expected properties
        _.where(
            // original list
            array_of_objects,
            // expected properties
            {'id':1, 'value':10}
        )
    )

【讨论】:

  • 通过在代码后面放置 4 个空格或将其放在 `` 字符内来缩进代码。
  • 谢谢!已经有一段时间了。
【解决方案5】:

我实际上可以想象我宁愿使用@kontr0l 方法而不是其他方法的情况,但是您必须了解这种方法是二次的,所以基本上这段代码是对幼稚方法的抽象 - 遍历两个数组中的所有值.

有比二次方更好的方法,我不会在这里使用任何大的 O 表示法,但这里有两种主要方法,两者都比简单的方法更好:

  • 遍历其中一个数组并使用二分搜索检查在已排序的第二个数组中是否存在。
  • 将值放入 set/hash/dictionary/你的名字。

正如已经提到的,如果您使用indexOf 方法的一些更灵活的类似物重新实现标准difference 方法,则可以对对象采用第一种方法。

使用第二种方法,我们可以碰壁,因为截至 2015 年 2 月,只有现代浏览器支持 Sets。由于 javascript 中的哈希(嗯,对象),它们只能有字符串类型的键,所以任何作为键调用的对象首先应该通过toString 方法进行转换。所以,我们需要提供一些 => 对应。在大多数情况下,在实践中它非常简单,例如,对于您的特定示例,此类对应关系可能只是 String(obj.id)

有了这样的对应关系,我们还可以使用以下 lodas/undercore 的方式:

var idsA = _.pluck(a, 'id');
var idsB = _.pluck(b, 'id');

// actually here we can stop in some cases, because 
// quite often we need to identify object, but not the object itself - 
// for instance to send some ids through remote API.
var intersect = _.intersection(idsA, idsB);

//to be 100% sure you get the idea, here we assume that object having equal ids are treated as equal, so does not really matter which of arrays we'll iterate:

var dictA = _.object(idsA, a); // now we can find a by id faster then with _.find
var intersectObj = intersect.map(function(id) {return dictA[id})

但是要承认稍微严格的限制——我们可以在我们的集合对象和自然数之间建立对应关系,我们可以建立更有效的算法,即我们所有的 id 都是非负整数——我们可以使用更有效的算法。

诀窍是通过这样引入两个辅助数组来实现 set:

var naturalSet = function (arr) {
    var sparse = [];
    var dense = [];

    var contains = function (i) {
        var res = sparse[i] < dense.length && dense[sparse[i]] == i;
        return res;
    }

    var add = function (v) {
        if (!contains(v)) {
            sparse[v] = dense.length;
            dense.push(v);
        }
    }

    arr.forEach(add);

    return {
        contains: contains,
        toArray: function () {
            return dense
        },
        _getDense: function () {
            return dense
        },
        _getSparse: function () {
            return sparse
        }
    }
}

那么我们就可以引入set with mapping to naturalSet了:

var set = function (arr, valueOf) {
    var natSet = naturalSet(arr.map(valueOf));
    return {
        contains: function (item) {
            return natSet.contains(valueOf(item))
        },
        toArray: function () {
            var sparse = natSet._getSparse();
            var res = natSet._getDense().map(function (i) {
                return arr[sparse[i]];
            });
            return res;
        }
    }
}

最后,我们可以引入交集:

var intersection = function(arr1, arr2, valueOf) {
   return set(arr2.filter(set(arr1, valueOf).contains), valueOf).toArray();
}

因此,依靠您正在处理的数据结构有时会对您有所帮助。

【讨论】:

    【解决方案6】:
    without using underscorejs,
    here is the pretty simple method i got solution ... 
    
    a = [{'key':'123'},{'key':'222'},{'key':'333'}]
    b = [{'key':'123'},{'key':'222'}]
    
    var diff = a.filter(function(item1) {
      for (var i in b) {
        if (item1.key === b[i].key) { return false; }
      };
      return true;
    });
    console.log('result',diff)
    

    【讨论】:

      【解决方案7】:

      除非我遗漏了什么,否则不明白为什么这些答案如此复杂?

      var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
      var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
      
      // Or use lodash _.differenceBy
      const difference = (array1, array2, prop = 'id') =>
        array1.filter(item1 =>
          !array2.some(item2 =>
            item2[prop] === item1[prop],
          ),
        );
        
      // In one array.
      console.log(difference(a, b));
      
      // Intersection.
      console.log([...difference(a, b), ...difference(b, a)]);

      【讨论】:

        【解决方案8】:
        var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
        var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
        
        var c = _.difference(a.map(e => e.id), b.map(e =>e.id));
        var array = [];
        array = a.map(e => {
           if(c.includes(e.id)){
             return e;
           }
        }).filter(r=>r);
        

        【讨论】:

        • 欢迎您!如果您解释您的方法相对于其他答案的优势,您的答案将更有帮助(并且更有可能获得支持)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-02
        • 1970-01-01
        • 2018-09-14
        • 1970-01-01
        • 2013-01-14
        • 2021-01-06
        相关资源
        最近更新 更多