How to use underscore's “intersection” on objects?

匿名 (未验证) 提交于 2019-12-03 09:02:45

问题:

_.intersection([], [])

only works with primitive types, right?

It doesn't work with objects. How can I make it work with objects (maybe by checking the "Id" field)?

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ] var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ]

In this example, the result should be:

_.intersection(a, b);

[ {'id': 1, 'name': 'jake' } ];

回答1:

You can create another function based on underscore's function. You only have to change one line of code from the original function:

_.intersectionObjects = function(array) {     var slice = Array.prototype.slice; // added this line as a utility     var rest = slice.call(arguments, 1);     return _.filter(_.uniq(array), function(item) {       return _.every(rest, function(other) {         //return _.indexOf(other, item) >= 0;         return _.any(other, function(element) { return _.isEqual(element, item); });       });     });   };

In this case you'd now be using underscore's isEqual() method instead of JavaScript's equality comparer. I tried it with your example and it worked. Here is an excerpt from underscore's documentation regarding the isEqual function:

_.isEqual(object, other)  Performs an optimized deep comparison between the two objects, to determine if they should be considered equal.

You can find the documentation here: http://documentcloud.github.com/underscore/#isEqual

I put up the code on jsFiddle so you can test and confirm it: http://jsfiddle.net/luisperezphd/jrJxT/



回答2:

Here is an alternative algorithm that should be flexible and perform better. One of those improvements is that you can specify your own comparison function so in your case you can just compare the id if it's a unique identifier.

function intersectionObjects2(a, b, areEqualFunction) {     var results = [];      for(var i = 0; i 

You can use it like this:

var a = [ { id: 1, name: 'jake' }, { id: 4, name: 'jenny'} ]; var b = [ { id: 1, name: 'jake' }, { id: 9, name: 'nick'} ]; var c = [ { id: 1, name: 'jake' }, { id: 4, name: 'jenny'}, { id: 9, name: 'nick'} ];  var result = intersectionObjects(a, b, c, function(item1, item2) {     return item1.id === item2.id; });

Or you can leave out the function and it will use underscores _.isEqual() function, like so:

var result = intersectionObjects(a, b, c);

You can find it on jsFiddle here: http://jsfiddle.net/luisperezphd/43vksdn6/



回答3:

The array methods in underscore are very powerful, you should only need a few lines to accomplish what you want to do:

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ]; var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];  var result = _(a).chain().map(function(ea) {     return _.find(b, function(eb) {return ea.id == eb.id;}); }).compact().value();

If you have large arrays you can get rid of the compact() call with one additional line:

var result = []; _.each(a, function(ea) {     var entry = _.find(b, function(eb) {return ea.id == eb.id;});     if (entry) result.push(entry); });


回答4:

I'd like to share my general solution for those cases.

I added a general function to underscore, using mixin, which performs a binary 'array' operation on two collections, according to a given Hash function:

_.mixin({     collectionOperation: function(arr1, arr2, hash, action) {         var iArr1 = _(arr1).indexBy(hash)             , iArr2 = _(arr2).indexBy(hash);         return action(_(iArr1).keys(), _(iArr2).keys()).map(function (id) {             return iArr1[id] || iArr2[id];         });     } });

Usage example:

_([{id:1,v:'q'},{id:2,v:'p'}]).collectionOperation([{id:3,v:'pq'}], 'id', _.union )

Note that 'id' may be replaced with a function.

I believe this solution is O(n+m).



回答5:

Technically, it does work on objects, but you need to be careful of reference equality.

var jake = {'id': 1, 'name': 'jake' },     jenny = {'id':4, 'name': 'jenny'},     nick =  {'id': 9, 'name': 'nick'}; var a = [jake, jenny] var b = [jake, nick];  _.intersection(a, b); // is [jake]


回答6:

In lodash 4.0.0. We can try like this

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ]; var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];  _.intersectionBy(a, b, 'id');

Output:

[ {'id': 1, 'name': 'jake' } ];



回答7:

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ]; var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];

Working function:

 function intersection(a,b){   var c=[];    for(m in a){       for(n in b){          if((a[m].id==a[n].id)&&(a[m].name==b[n].name))                  c.push(a[m]);                 }}     return c;   } console.log(intersection(a,b));

I have also tried code in jQuery specially after Pointy's suggestion. Compare has to be customizable as per the structure of JSON object.



回答8:

If you wanna compare only objects:

b = {"1":{"prod":"fibaro"},"2":{"prod":"aeotec"},"3":{"prod":"sw"}};  a = {"1":{"prod":"fibaro"}};   _.intersectObjects = function(a,b){     var m = Object.keys(a).length;     var n = Object.keys(b).length;     var output;     if (m > n) output = _.clone(a); else output = _.clone(b);      var keys = _.xor(_.keys(a),_.keys(b));     for(k in keys){         console.log(k);         delete output[keys[k]];     }     return output; } _.intersectObjects(a,b); // this returns { '1': { prod: 'fibaro' } }


回答9:

var a = {a:'a1',b:'b1'},     b = {a:'a2',b:'b2',c:'c2'};  _.pick(a,_.intersection(_.keys(a),_.keys(b)));  // {a:'a1',b:'b1'}


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!