diff --git a/lodash.js b/lodash.js index 57b247b72..8a7ff787d 100644 --- a/lodash.js +++ b/lodash.js @@ -33,7 +33,7 @@ var keyPrefix = +new Date + ''; /** Used as the size when optimizations are enabled for large arrays */ - var largeArraySize = 200; + var largeArraySize = 75; /** Used to match empty string literals in compiled template source */ var reEmptyStringLeading = /\b__p \+= '';/g, @@ -662,26 +662,72 @@ * @param {Mixed} value The value to search for. * @returns {Boolean} Returns `true`, if `value` is found, else `false`. */ - function cachedContains(array) { - var length = array.length, - isLarge = length >= largeArraySize; + function createCache(array) { + var bailout, + index = -1, + length = array.length, + isLarge = length >= largeArraySize, + objCache = {}; - if (isLarge) { - var cache = {}, - index = -1; + var caches = { + 'false': false, + 'function': false, + 'null': false, + 'number': {}, + 'object': objCache, + 'string': {}, + 'true': false, + 'undefined': false + }; - while (++index < length) { - var key = keyPrefix + array[index]; - (cache[key] || (cache[key] = [])).push(array[index]); + function cacheContains(value) { + var type = typeof value; + if (type == 'boolean' || value == null) { + return caches[value]; + } + var cache = caches[type] || (type = 'object', objCache), + key = type == 'number' ? value : keyPrefix + value; + + return type == 'object' + ? (cache[key] ? indexOf(cache[key], value) > -1 : false) + : !!cache[key]; + } + + function cachePush(value) { + var type = typeof value; + if (type == 'boolean' || value == null) { + caches[value] = true; + } else { + var cache = caches[type] || (type = 'object', objCache), + key = type == 'number' ? value : keyPrefix + value; + + if (type == 'object') { + bailout = (cache[key] || (cache[key] = [])).push(value) == length; + } else { + cache[key] = true; + } } } - return function(value) { - if (isLarge) { - var key = keyPrefix + value; - return cache[key] && indexOf(cache[key], value) > -1; - } + + function simpleContains(value) { return indexOf(array, value) > -1; } + + function simplePush(value) { + array.push(value); + } + + if (isLarge) { + while (++index < length) { + cachePush(array[index]); + } + if (bailout) { + isLarge = caches = objCache = null; + } + } + return isLarge + ? { 'contains': cacheContains, 'push': cachePush } + : { 'push': simplePush, 'contains' : simpleContains }; } /** @@ -3441,7 +3487,7 @@ var index = -1, length = array ? array.length : 0, flattened = concat.apply(arrayProto, nativeSlice.call(arguments, 1)), - contains = cachedContains(flattened), + contains = createCache(flattened).contains, result = []; while (++index < length) { @@ -3769,29 +3815,21 @@ function intersection(array) { var args = arguments, argsLength = args.length, - cache = { '0': {} }, + cache = createCache([]), + caches = {}, index = -1, length = array ? array.length : 0, isLarge = length >= largeArraySize, - result = [], - seen = result; + result = []; outer: while (++index < length) { var value = array[index]; - if (isLarge) { - var key = keyPrefix + value; - var inited = cache[0][key] - ? !(seen = cache[0][key]) - : (seen = cache[0][key] = []); - } - if (inited || indexOf(seen, value) < 0) { - if (isLarge) { - seen.push(value); - } + if (!cache.contains(value)) { var argsIndex = argsLength; + cache.push(value); while (--argsIndex) { - if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex])))(value)) { + if (!(caches[argsIndex] || (caches[argsIndex] = createCache(args[argsIndex]).contains))(value)) { continue outer; } } @@ -4179,26 +4217,20 @@ } // init value cache for large arrays var isLarge = !isSorted && length >= largeArraySize; - if (isLarge) { - var cache = {}; - } if (callback != null) { seen = []; callback = lodash.createCallback(callback, thisArg); } + if (isLarge) { + seen = createCache([]); + } while (++index < length) { var value = array[index], computed = callback ? callback(value, index, array) : value; - if (isLarge) { - var key = keyPrefix + computed; - var inited = cache[key] - ? !(seen = cache[key]) - : (seen = cache[key] = []); - } if (isSorted ? !index || seen[seen.length - 1] !== computed - : inited || indexOf(seen, computed) < 0 + : (isLarge ? !seen.contains(computed) : indexOf(seen, computed) < 0) ) { if (callback || isLarge) { seen.push(computed); diff --git a/perf/perf.js b/perf/perf.js index dd27c2ce2..cfcfeedbf 100644 --- a/perf/perf.js +++ b/perf/perf.js @@ -775,6 +775,18 @@ ) ); + suites.push( + Benchmark.Suite('`_.difference` iterating 75 elements') + .add(buildName, { + 'fn': 'lodash.difference(seventyFiveValues, seventyFiveValues2)', + 'teardown': 'function multiArrays(){}' + }) + .add(otherName, { + 'fn': '_.difference(seventyFiveValues, seventyFiveValues2)', + 'teardown': 'function multiArrays(){}' + }) + ); + suites.push( Benchmark.Suite('`_.difference` iterating 200 elements') .add(buildName, { @@ -1076,6 +1088,18 @@ ) ); + suites.push( + Benchmark.Suite('`_.intersection` iterating 75 elements') + .add(buildName, { + 'fn': 'lodash.intersection(seventyFiveValues, seventyFiveValues2)', + 'teardown': 'function multiArrays(){}' + }) + .add(otherName, { + 'fn': '_.intersection(seventyFiveValues, seventyFiveValues2)', + 'teardown': 'function multiArrays(){}' + }) + ); + suites.push( Benchmark.Suite('`_.intersection` iterating 200 elements') .add(buildName, { @@ -1731,11 +1755,23 @@ suites.push( Benchmark.Suite('`_.union` iterating an array of 75 elements') .add(buildName, { - 'fn': 'lodash.union(fiftyValues, twentyFiveValues2)', + 'fn': 'lodash.union(twentyFiveValues, fiftyValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { - 'fn': '_.union(fiftyValues, twentyFiveValues2)', + 'fn': '_.union(twentyFiveValues, fiftyValues2)', + 'teardown': 'function multiArrays(){}' + }) + ); + + suites.push( + Benchmark.Suite('`_.union` iterating an array of 200 elements') + .add(buildName, { + 'fn': 'lodash.union(oneHundredValues, oneHundredValues2)', + 'teardown': 'function multiArrays(){}' + }) + .add(otherName, { + 'fn': '_.union(oneHundredValues, oneHundredValues2)', 'teardown': 'function multiArrays(){}' }) ); @@ -1766,6 +1802,18 @@ ) ); + suites.push( + Benchmark.Suite('`_.uniq` iterating an array of 75 elements') + .add(buildName, { + 'fn': 'lodash.uniq(twentyFiveValues.concat(fiftyValues2))', + 'teardown': 'function multiArrays(){}' + }) + .add(otherName, { + 'fn': '_.uniq(twentyFiveValues.concat(fiftyValues2))', + 'teardown': 'function multiArrays(){}' + }) + ); + suites.push( Benchmark.Suite('`_.uniq` iterating an array of 200 elements') .add(buildName, {