在对象数组上使用下划线的“差异”方法
_.difference([], [])
这个方法工作正常,当我有像原始types的数据
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}];
似乎没有工作
原因就是具有相同内容的对象不是同一个对象,例如
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
因此不会find对象,除非它们指向相同的对象。
您可以通过遍历所有项目并调用比较callback来改进下划线_.contains
,您应该能够传递差异或包含函数,或者您可以检查包含方法的改进版本
试试这个大小来find一个对象数组的差异:
var test = [{a: 1},{b: 2}]; var test2 = [{a: 1}]; _.filter(test, function(obj){ return !_.findWhere(test2, obj); });
虽然接受的答案是正确的,其他答案也给出了好的想法,但还有一个额外的选项,用下划线很容易实现。
这个解决scheme依赖于每个对象都有一个唯一的ID,但是在很多情况下这是真实的,你可以用两行代码来获得两个对象数组的差异。
使用下划线的“pluck”方法,可以快速构build源数据集和目标数据集中所有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方法的情况,而不是其他的,但是你必须明白这个方法是二次的,所以基本上这个代码是一个抽象的方法 – 遍历两个数组中的所有值。
有更好的方法比二次方,我不会在这里使用任何大的O符号,但这里有两个主要的方法,都是更好的然后天真的一个:
- 迭代通过其中一个数组,并使用二分查找检查是否存在于sorting的第二个数组中。
- 把值放入set / hash / dictionary /你的名字。
正如已经提到的那样,如果使用一些更灵活的indexOf
方法来实现标准的difference
方法,那么对象可以采用第一种方法。
用第二种方法,我们可以打开墙,事实上,截至2015年2月,只有现代浏览器支持集 。 作为JavaScript中的散列(好,对象),它们只能有stringtypes的键,所以任何被调用为键的对象都应该通过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})
但是,购买承认稍微严格的限制 – 我们可以build立我们的集合对象和自然数之间的对应,我们可以build立更有效的algorithm,即我们所有的ID都是非负整数 – 我们可以使用更有效的algorithm。
诀窍是通过这种方式引入两个辅助数组来实现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到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(); }
所以,依靠你正在工作的数据结构可以帮助你。
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)
原谅我在这里晚些时候跳,但这可能有所帮助:
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} ) )