Reputation: 1733
Provided I have two collections:
c1 - [{a:1},{a:2},{a:3}]
and
c2 - [{a:1},{a:7},{a:8}]
what's the fastest way to add unique items from c2
into c1
using Underscore.JS
? The real numbers in collections would be 2K
for c1
and 500
for c2
, the operation is performed often, so must be performant!
Update 1 - I'm only using Underscore.JS
for a few days, I couldn't find a way to add one collection into another (I can filter c2
myself) - is that trivial in Underscore.JS
?
Upvotes: 32
Views: 60160
Reputation: 29756
Try:
_.uniq(_.union(c1, c2), false, _.property('a'))
In detail:
_.union(*arrays)
Computes the union of the passed-in arrays.
_.property(key)
(since Version 1.6.0)
Returns a function that will itself return the key property of any passed-in object.
_.uniq(array, [isSorted], [iteratee])
Produces a duplicate-free version of the array, using
===
to test object equality. If you know in advance that the array is sorted, passingtrue
for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iteratee function.
Upvotes: 16
Reputation: 17381
The documentation for uniq()
function mentions that it runs much faster if the list is sorted. Also using the chained calls can improve readability. So you can do:
_.chain(c1).union(c2).sortBy("a").uniq(true, function(item){ return item.a; }).value();
Or if you prefer the unchained version (which is 11 characters shorter but less readable):
_.uniq(_.sortBy(_.union(c1,c2),"a"),true, function(item){ return item.a; });
The documentation and examples for uniq()
don't make it clear how the callback function works. The algorithm for uniq()
function calls this function on every element from both lists. If the result of this function is the same, it removes that element (assuming it is duplicated).
union()
in fact prevents duplicates when called on an array. We can use this fact too:
_.map(_.union(_.pluck(c1,"a"),_.pluck(c2,"a")),function (item) {return {a:item};});
The above like first converts the list of objects to simple arrays (pluck()
) then combines them using union()
and eventually uses map()
to make a list of objects.
Reference: uniq()
Upvotes: 6
Reputation: 17381
Since there is a huge number of properties in both objects and this algorithm runs often, it would be better to use core Javascript instead of any library:
//adds all new properties from the src to dst. If the property already exists, updates the number in dst. dst and src are objects
function extendNumberSet( dst, src ) {
var allVals = [];
for ( var i = 0; i < dst.length; i++ ) {
allVals.push(dst[i].a);
}
for ( var i = 0; i < src.length; i++ ) {
if ( allVals.indexOf( src[i].a ) === -1 ) {
dst.push( src[i] );
}
}
}
here is a JSfiddle to test it.
Upvotes: 1
Reputation: 5348
The following will:
Note that this would work only if all your objects have the property a
.
_.uniq(_.union(c1, c2), false, function(item, key, a){ return item.a; });
You can find other options in this question.
Upvotes: 53