可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
_.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 < a.length; i++) {
var aElement = a[i];
var existsInB = _.any(b, function(bElement) { return areEqualFunction(bElement, aElement); });
if(existsInB) {
results.push(aElement);
}
}
return results;
}
function intersectionObjects() {
var results = arguments[0];
var lastArgument = arguments[arguments.length - 1];
var arrayCount = arguments.length;
var areEqualFunction = _.isEqual;
if(typeof lastArgument === "function") {
areEqualFunction = lastArgument;
arrayCount--;
}
for(var i = 1; i < arrayCount ; i++) {
var array = arguments[i];
results = intersectionObjects2(results, array, areEqualFunction);
if(results.length === 0) break;
}
return results;
}
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.
<script type="text/javascript">
jQuery(document).ready(function(){
var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ];
var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];
var c=[];
jQuery.each(a, function(ka,va) {
jQuery.each(b, function(kb,vb) {
if(compare(va,vb))
c.push(va);
});
});
console.log(c);
});
function compare(a,b){
if(a.id==b.id&&a.name==b.name)
return true;
else return false;
}
</script>
回答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'}