diff --git a/lodash.js b/lodash.js index 5afd69f01..df908c422 100644 --- a/lodash.js +++ b/lodash.js @@ -2380,25 +2380,6 @@ return high; } - /** - * The implementation of `_.uniq` optimized for sorted arrays - * - * @private - * @param {Array} array The array to inspect. - * @returns {Array} Returns the new duplicate-value-free array. - */ - function sortedUniq(array) { - var result = [], - index = -1, - length = array ? array.length : 0; - while (++index < length) { - if (index + 1 > length || array[index] !== array[index + 1]) { - result.push(array[index]); - } - } - return result; - }; - /** * The base implementation of `_.uniq` without support for callback shorthands * and `this` binding. @@ -2409,12 +2390,9 @@ * @returns {Array} Returns the new duplicate-value-free array. */ function baseUniq(array, iterator) { - var length = array ? array.length : 0; - if (!length) { - return []; - } var index = -1, indexOf = getIndexOf(), + length = array.length, prereq = indexOf === baseIndexOf, isLarge = prereq && createCache && length >= 200, isCommon = prereq && !isLarge, @@ -3033,6 +3011,33 @@ return result; } + /** + * An implementation of `_.uniq` optimized for sorted arrays without support + * for callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iterator] The function called per iteration. + * @returns {Array} Returns the new duplicate-value-free array. + */ + function sortedUniq(array, iterator) { + var seen, + index = -1, + length = array.length, + result = []; + + while (++index < length) { + var value = array[index], + computed = iterator ? iterator(value, index, array) : value; + + if (!index || seen !== computed) { + seen = computed; + result.push(value); + } + } + return result; + } + /** * Converts `collection` to an array if it is not an array-like value. * @@ -4106,23 +4111,28 @@ * // => [{ 'x': 1 }, { 'x': 2 }] */ function uniq(array, isSorted, iterator, thisArg) { + var length = array ? array.length : 0; + if (!length) { + return []; + } // juggle arguments var type = typeof isSorted; if (type != 'boolean' && isSorted != null) { thisArg = iterator; iterator = isSorted; + isSorted = false; // enables use as a callback for functions like `_.map` if ((type == 'number' || type == 'string') && thisArg && thisArg[iterator] === array) { iterator = null; } - } else if (isSorted && getIndexOf() == baseIndexOf) { - return sortedUniq(array); } if (iterator != null) { iterator = getCallback(iterator, thisArg, 3); } - return baseUniq(array, iterator); + return (isSorted && getIndexOf() == baseIndexOf) + ? sortedUniq(array, iterator) + : baseUniq(array, iterator); } /** diff --git a/test/test.js b/test/test.js index ba52874f5..1a0e651c8 100644 --- a/test/test.js +++ b/test/test.js @@ -10683,19 +10683,24 @@ deepEqual(_.uniq(objects), objects); }); - test('should work with `isSorted`', 4, function() { - deepEqual(_.uniq([1, 1, 2, 2, 3], true), [1, 2, 3]); - deepEqual(_.uniq(_.range(100), true), _.range(100)); - deepEqual(_.uniq(_.times(100, _.constant(undefined))), [undefined]); - deepEqual(_.uniq([1, 2, 3, 3, 3, 3, 3]), [1, 2, 3]); + test('should work with `isSorted`', 3, function() { + var expected = [1, 2, 3]; + deepEqual(_.uniq([1, 2, 3], true), expected); + deepEqual(_.uniq([1, 1, 2, 2, 3], true), expected); + deepEqual(_.uniq([1, 2, 3, 3, 3, 3, 3], true), expected); }); - test('should work with a callback', 1, function() { - var actual = _.uniq(objects, false, function(object) { - return object.a; - }); + test('should work with a callback', 2, function() { + _.each([objects, _.sortBy(objects, 'a')], function(array, index) { + var isSorted = !!index, + expected = isSorted ? [objects[2], objects[0], objects[1]] : objects.slice(0, 3); - deepEqual(actual, objects.slice(0, 3)); + var actual = _.uniq(array, isSorted, function(object) { + return object.a; + }); + + deepEqual(actual, expected); + }); }); test('should work with a callback without specifying `isSorted`', 1, function() {