mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-14 04:37:50 +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
|
* Creates compiled iteration functions. The iteration function will be created
|
||||||
* to iterate over only objects if the first argument of `options.args` is
|
* to iterate over only objects if the first argument of `options.args` is
|
||||||
@@ -1224,10 +1259,11 @@
|
|||||||
}
|
}
|
||||||
var index = -1,
|
var index = -1,
|
||||||
length = array.length,
|
length = array.length,
|
||||||
flattened = concat.apply(result, arguments);
|
flattened = concat.apply(result, arguments),
|
||||||
|
contains = cachedContains(flattened, length);
|
||||||
|
|
||||||
while (++index < length) {
|
while (++index < length) {
|
||||||
if (indexOf(flattened, array[index], length) < 0) {
|
if (!contains(array[index])) {
|
||||||
result.push(array[index]);
|
result.push(array[index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1390,12 +1426,15 @@
|
|||||||
var value,
|
var value,
|
||||||
index = -1,
|
index = -1,
|
||||||
length = array.length,
|
length = array.length,
|
||||||
others = slice.call(arguments, 1);
|
others = slice.call(arguments, 1),
|
||||||
|
cache = [];
|
||||||
|
|
||||||
while (++index < length) {
|
while (++index < length) {
|
||||||
value = array[index];
|
value = array[index];
|
||||||
if (indexOf(result, value) < 0 &&
|
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);
|
result.push(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1847,10 +1886,11 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
var index = -1,
|
var index = -1,
|
||||||
length = array.length;
|
length = array.length,
|
||||||
|
contains = cachedContains(arguments, 1, 20);
|
||||||
|
|
||||||
while (++index < length) {
|
while (++index < length) {
|
||||||
if (indexOf(arguments, array[index], 1) < 0) {
|
if (!contains(array[index])) {
|
||||||
result.push(array[index]);
|
result.push(array[index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2774,7 +2814,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// objects with different constructors are not equivalent
|
// objects with different constructors are not equivalent
|
||||||
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) {
|
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
249
perf/perf.js
249
perf/perf.js
@@ -95,7 +95,6 @@
|
|||||||
belt = this.name == 'Lo-Dash' ? lodash : _;
|
belt = this.name == 'Lo-Dash' ? lodash : _;
|
||||||
|
|
||||||
var index,
|
var index,
|
||||||
length,
|
|
||||||
limit = 20,
|
limit = 20,
|
||||||
object = {},
|
object = {},
|
||||||
objects = Array(limit),
|
objects = Array(limit),
|
||||||
@@ -104,7 +103,7 @@
|
|||||||
nestedNumbers = [1, [2], [3, [[4]]]],
|
nestedNumbers = [1, [2], [3, [[4]]]],
|
||||||
twoNumbers = [12, 21];
|
twoNumbers = [12, 21];
|
||||||
|
|
||||||
for (index = 0, length = limit; index < length; index++) {
|
for (index = 0; index < limit; index++) {
|
||||||
numbers[index] = index;
|
numbers[index] = index;
|
||||||
object['key' + index] = index;
|
object['key' + index] = index;
|
||||||
objects[index] = { 'num': index };
|
objects[index] = { 'num': index };
|
||||||
@@ -132,7 +131,7 @@
|
|||||||
funcNames = belt.functions(lodash);
|
funcNames = belt.functions(lodash);
|
||||||
|
|
||||||
// potentially expensive
|
// potentially expensive
|
||||||
for (index = 0, length = this.count; index < length; index++) {
|
for (index = 0; index < this.count; index++) {
|
||||||
bindAllObjects[index] = belt.clone(lodash);
|
bindAllObjects[index] = belt.clone(lodash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,13 +186,50 @@
|
|||||||
numbers2 = Array(limit),
|
numbers2 = Array(limit),
|
||||||
nestedNumbers2 = [1, [2], [3, [[4]]]];
|
nestedNumbers2 = [1, [2], [3, [[4]]]];
|
||||||
|
|
||||||
for (index = 0, length = limit; index < length; index++) {
|
for (index = 0; index < limit; index++) {
|
||||||
numbers2[index] = index;
|
numbers2[index] = index;
|
||||||
object2['key' + index] = index;
|
object2['key' + index] = index;
|
||||||
objects2[index] = { 'num': 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') {
|
if (typeof template != 'undefined') {
|
||||||
var tplData = {
|
var tplData = {
|
||||||
'header1': 'Header1',
|
'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(
|
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(
|
suites.push(
|
||||||
@@ -759,7 +891,6 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
suites.push(
|
suites.push(
|
||||||
Benchmark.Suite('`_.isEqual` comparing primitives and objects (edge case)')
|
Benchmark.Suite('`_.isEqual` comparing primitives and objects (edge case)')
|
||||||
.add('Lo-Dash', {
|
.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(
|
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 + '') {
|
if (Benchmark.platform + '') {
|
||||||
log(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');
|
QUnit.module('lodash.escape');
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user