mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-01-29 06:27:49 +00:00
Add optimizations for large arrays to _.difference, _.intersection, and _.without.
Former-commit-id: 26d55a6a3340e77b5269b2003d20def3fe77bca9
This commit is contained in:
55
lodash.js
55
lodash.js
@@ -439,6 +439,41 @@
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Creates a new function optimized for searching large arrays for a given `value`,
|
||||
* starting at `fromIndex`, using strict equality for comparisons, i.e. `===`.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} array The array to search.
|
||||
* @param {Mixed} value The value to search for.
|
||||
* @param {Number} [fromIndex=0] The index to start searching from.
|
||||
* @param {Number} [largeSize=30] The length at which an array is considered large.
|
||||
* @returns {Boolean} Returns `true` if `value` is found, else `false`.
|
||||
*/
|
||||
function cachedContains(array, fromIndex, largeSize) {
|
||||
fromIndex || (fromIndex = 0);
|
||||
|
||||
var length = array.length,
|
||||
isLarge = (length - fromIndex) >= (largeSize || 30),
|
||||
cache = isLarge ? {} : array;
|
||||
|
||||
if (isLarge) {
|
||||
// init value cache
|
||||
var value,
|
||||
index = fromIndex - 1;
|
||||
|
||||
while (++index < length) {
|
||||
value = array[index];
|
||||
(hasOwnProperty.call(cache, value) ? cache[value] : (cache[value] = [])).push(value);
|
||||
}
|
||||
}
|
||||
return function(value) {
|
||||
return isLarge
|
||||
? hasOwnProperty.call(cache, value) && indexOf(cache[value], value) > -1
|
||||
: indexOf(cache, value, fromIndex) > -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates compiled iteration functions. The iteration function will be created
|
||||
* to iterate over only objects if the first argument of `options.args` is
|
||||
@@ -1224,10 +1259,11 @@
|
||||
}
|
||||
var index = -1,
|
||||
length = array.length,
|
||||
flattened = concat.apply(result, arguments);
|
||||
flattened = concat.apply(result, arguments),
|
||||
contains = cachedContains(flattened, length);
|
||||
|
||||
while (++index < length) {
|
||||
if (indexOf(flattened, array[index], length) < 0) {
|
||||
if (!contains(array[index])) {
|
||||
result.push(array[index]);
|
||||
}
|
||||
}
|
||||
@@ -1390,12 +1426,15 @@
|
||||
var value,
|
||||
index = -1,
|
||||
length = array.length,
|
||||
others = slice.call(arguments, 1);
|
||||
others = slice.call(arguments, 1),
|
||||
cache = [];
|
||||
|
||||
while (++index < length) {
|
||||
value = array[index];
|
||||
if (indexOf(result, value) < 0 &&
|
||||
every(others, function(other) { return indexOf(other, value) > -1; })) {
|
||||
every(others, function(other, index) {
|
||||
return (cache[index] || (cache[index] = cachedContains(other)))(value);
|
||||
})) {
|
||||
result.push(value);
|
||||
}
|
||||
}
|
||||
@@ -1847,10 +1886,11 @@
|
||||
return result;
|
||||
}
|
||||
var index = -1,
|
||||
length = array.length;
|
||||
length = array.length,
|
||||
contains = cachedContains(arguments, 1, 20);
|
||||
|
||||
while (++index < length) {
|
||||
if (indexOf(arguments, array[index], 1) < 0) {
|
||||
if (!contains(array[index])) {
|
||||
result.push(array[index]);
|
||||
}
|
||||
}
|
||||
@@ -2774,7 +2814,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// objects with different constructors are not equivalent
|
||||
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) {
|
||||
return false;
|
||||
|
||||
249
perf/perf.js
249
perf/perf.js
@@ -95,7 +95,6 @@
|
||||
belt = this.name == 'Lo-Dash' ? lodash : _;
|
||||
|
||||
var index,
|
||||
length,
|
||||
limit = 20,
|
||||
object = {},
|
||||
objects = Array(limit),
|
||||
@@ -104,7 +103,7 @@
|
||||
nestedNumbers = [1, [2], [3, [[4]]]],
|
||||
twoNumbers = [12, 21];
|
||||
|
||||
for (index = 0, length = limit; index < length; index++) {
|
||||
for (index = 0; index < limit; index++) {
|
||||
numbers[index] = index;
|
||||
object['key' + index] = index;
|
||||
objects[index] = { 'num': index };
|
||||
@@ -132,7 +131,7 @@
|
||||
funcNames = belt.functions(lodash);
|
||||
|
||||
// potentially expensive
|
||||
for (index = 0, length = this.count; index < length; index++) {
|
||||
for (index = 0; index < this.count; index++) {
|
||||
bindAllObjects[index] = belt.clone(lodash);
|
||||
}
|
||||
}
|
||||
@@ -187,13 +186,50 @@
|
||||
numbers2 = Array(limit),
|
||||
nestedNumbers2 = [1, [2], [3, [[4]]]];
|
||||
|
||||
for (index = 0, length = limit; index < length; index++) {
|
||||
for (index = 0; index < limit; index++) {
|
||||
numbers2[index] = index;
|
||||
object2['key' + index] = index;
|
||||
objects2[index] = { 'num': index };
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof multiArrays != 'undefined') {
|
||||
var twentyFiveValues = Array(25),
|
||||
twentyFiveValues2 = Array(25),
|
||||
fiftyValues = Array(50),
|
||||
fiftyValues2 = Array(50),
|
||||
seventyFiveValues = Array(75),
|
||||
seventyFiveValues2 = Array(75),
|
||||
lowerChars = 'abcdefghijklmnopqrstuvwxyz'.split(''),
|
||||
upperChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
||||
|
||||
for (index = 0; index < 75; index++) {
|
||||
if (index < 26) {
|
||||
if (index < 20) {
|
||||
twentyFiveValues[index] = lowerChars[index];
|
||||
twentyFiveValues2[index] = upperChars[index];
|
||||
}
|
||||
else if (index < 25) {
|
||||
twentyFiveValues[index] =
|
||||
twentyFiveValues2[index] = index;
|
||||
}
|
||||
fiftyValues[index] =
|
||||
seventyFiveValues[index] = lowerChars[index];
|
||||
|
||||
fiftyValues2[index] =
|
||||
seventyFiveValues2[index] = upperChars[index];
|
||||
}
|
||||
else {
|
||||
if (index < 50) {
|
||||
fiftyValues[index] = index;
|
||||
fiftyValues2[index] = index + (index < 40 ? 75 : 0);
|
||||
}
|
||||
seventyFiveValues[index] = index;
|
||||
seventyFiveValues2[index] = index + (index < 60 ? 75 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof template != 'undefined') {
|
||||
var tplData = {
|
||||
'header1': 'Header1',
|
||||
@@ -489,6 +525,54 @@
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.difference` iterating 25 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.difference(twentyFiveValues, twentyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.difference(twentyFiveValues, twentyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.difference` iterating 50 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.difference(fiftyValues, fiftyValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.difference(fiftyValues, fiftyValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.difference` iterating 75 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.difference(seventyFiveValues, seventyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.difference(seventyFiveValues, seventyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
suites.push(
|
||||
@@ -725,6 +809,54 @@
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.intersection` iterating 25 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.intersection(twentyFiveValues, twentyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.intersection(twentyFiveValues, twentyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.intersection` iterating 50 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.intersection(fiftyValues, fiftyValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.intersection(fiftyValues, fiftyValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.intersection` iterating 75 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.intersection(seventyFiveValues, seventyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.intersection(seventyFiveValues, seventyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
suites.push(
|
||||
@@ -759,7 +891,6 @@
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.isEqual` comparing primitives and objects (edge case)')
|
||||
.add('Lo-Dash', {
|
||||
@@ -1218,6 +1349,54 @@
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.union` iterating an array of 25 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.union(twentyFiveValues, twentyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.union(twentyFiveValues, twentyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.union` iterating an array of 50 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.union(fiftyValues, fiftyValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.union(fiftyValues, fiftyValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.union` iterating an array of 75 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.union(seventyFiveValues, seventyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.union(seventyFiveValues, seventyFiveValues2);
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
suites.push(
|
||||
@@ -1258,6 +1437,66 @@
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.without`')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.without(numbers, 9, 12, 14, 15);
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.without(numbers, 9, 12, 14, 15);
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.without` iterating an array of 25 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.without.apply(lodash, [twentyFiveValues].concat(twentyFiveValues2));
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.without.apply(_, [twentyFiveValues].concat(twentyFiveValues2));
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.without` iterating an array of 50 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.without.apply(lodash, [fiftyValues].concat(fiftyValues2));
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.without.apply(_, [fiftyValues].concat(fiftyValues2));
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.without` iterating an array of 75 elements')
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.without.apply(lodash, [seventyFiveValues].concat(seventyFiveValues2));
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.without.apply(_, [seventyFiveValues].concat(seventyFiveValues2));
|
||||
},
|
||||
'teardown': 'function multiArrays(){}'
|
||||
})
|
||||
);
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
if (Benchmark.platform + '') {
|
||||
log(Benchmark.platform + '');
|
||||
}
|
||||
|
||||
19
test/test.js
19
test/test.js
@@ -166,6 +166,25 @@
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.difference');
|
||||
|
||||
(function() {
|
||||
test('should work correctly when using `cachedContains`', function() {
|
||||
var array1 = _.range(27),
|
||||
array2 = array1.slice(),
|
||||
a = {},
|
||||
b = {},
|
||||
c = {};
|
||||
|
||||
array1.push(a, b, c);
|
||||
array2.push(b, c, a);
|
||||
|
||||
deepEqual(_.difference(array1, array2), []);
|
||||
});
|
||||
}());
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.escape');
|
||||
|
||||
(function() {
|
||||
|
||||
Reference in New Issue
Block a user