Split out _.sortedIndex and _.sortedLastIndex into _.sortedIndexBy and _.sortedLastIndexBy.

This commit is contained in:
jdalton
2015-07-06 08:11:19 -07:00
committed by John-David Dalton
parent bc7cabde4e
commit 41f248f54d
2 changed files with 94 additions and 53 deletions

View File

@@ -5044,10 +5044,30 @@
/**
* Uses a binary search to determine the lowest index at which `value` should
* be inserted into `array` in order to maintain its sort order. If an iteratee
* function is provided it's invoked for `value` and each element of `array`
* to compute their sort ranking. The iteratee is invoked with one argument:
* (value).
* be inserted into `array` in order to maintain its sort order.
*
* @static
* @memberOf _
* @category Array
* @param {Array} array The sorted array to inspect.
* @param {*} value The value to evaluate.
* @returns {number} Returns the index at which `value` should be inserted into `array`.
* @example
*
* _.sortedIndex([30, 50], 40);
* // => 1
*
* _.sortedIndex([4, 5], 4);
* // => 0
*/
function sortedIndex(array, value) {
return binaryIndex(array, value);
}
/**
* This method is like `_.sortedIndex` except that it accepts an iteratee
* which is invoked for `value` and each element of `array` to compute their
* sort ranking. The iteratee is invoked with one argument: (value).
*
* @static
* @memberOf _
@@ -5058,27 +5078,17 @@
* @returns {number} Returns the index at which `value` should be inserted into `array`.
* @example
*
* _.sortedIndex([30, 50], 40);
* // => 1
*
* _.sortedIndex([4, 4, 5, 5], 5);
* // => 2
*
* var dict = { 'thirty': 30, 'forty': 40, 'fifty': 50 };
*
* // using an iteratee function
* _.sortedIndex(['thirty', 'fifty'], 'forty', _.propertyOf(dict));
* _.sortedIndexBy(['thirty', 'fifty'], 'forty', _.propertyOf(dict));
* // => 1
*
* // using the `_.property` callback shorthand
* _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
* // => 1
* _.sortedIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, 'x');
* // => 0
*/
function sortedIndex(array, value, iteratee) {
var toIteratee = getIteratee();
return (iteratee == null && toIteratee === baseIteratee)
? binaryIndex(array, value)
: binaryIndexBy(array, value, toIteratee(iteratee));
function sortedIndexBy(array, value, iteratee) {
return binaryIndexBy(array, value, getIteratee(iteratee));
}
/**
@@ -5091,18 +5101,36 @@
* @category Array
* @param {Array} array The sorted array to inspect.
* @param {*} value The value to evaluate.
* @returns {number} Returns the index at which `value` should be inserted into `array`.
* @example
*
* _.sortedLastIndex([4, 5], 4);
* // => 1
*/
function sortedLastIndex(array, value) {
return binaryIndex(array, value, true);
}
/**
* This method is like `_.sortedLastIndex` except that it accepts an iteratee
* which is invoked for `value` and each element of `array` to compute their
* sort ranking. The iteratee is invoked with one argument: (value).
*
* @static
* @memberOf _
* @category Array
* @param {Array} array The sorted array to inspect.
* @param {*} value The value to evaluate.
* @param {Function|Object|string} [iteratee=_.identity] The function invoked per iteration.
* @returns {number} Returns the index at which `value` should be inserted into `array`.
* @example
*
* _.sortedLastIndex([4, 4, 5, 5], 5);
* // => 4
* // using the `_.property` callback shorthand
* _.sortedLastIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, 'x');
* // => 1
*/
function sortedLastIndex(array, value, iteratee) {
var toIteratee = getIteratee();
return (iteratee == null && toIteratee === baseIteratee)
? binaryIndex(array, value, true)
: binaryIndexBy(array, value, toIteratee(iteratee), true);
function sortedLastIndexBy(array, value, iteratee) {
return binaryIndexBy(array, value, getIteratee(iteratee), true);
}
/**
@@ -11526,7 +11554,9 @@
lodash.snakeCase = snakeCase;
lodash.some = some;
lodash.sortedIndex = sortedIndex;
lodash.sortedIndexBy = sortedIndexBy;
lodash.sortedLastIndex = sortedLastIndex;
lodash.sortedLastIndexBy = sortedLastIndexBy;
lodash.startCase = startCase;
lodash.startsWith = startsWith;
lodash.sum = sum;

View File

@@ -8739,12 +8739,12 @@
}
});
test('`_.sortedIndex` should use `_.iteratee` internally', 1, function() {
test('`_.sortedIndexBy` should use `_.iteratee` internally', 1, function() {
if (!isModularize) {
var objects = [{ 'a': 30 }, { 'a': 50 }];
_.iteratee = getPropA;
strictEqual(_.sortedIndex(objects, { 'a': 40 }), 1);
strictEqual(_.sortedIndexBy(objects, { 'a': 40 }), 1);
_.iteratee = iteratee;
}
else {
@@ -8752,12 +8752,12 @@
}
});
test('`_.sortedLastIndex` should use `_.iteratee` internally', 1, function() {
test('`_.sortedLastIndexBy` should use `_.iteratee` internally', 1, function() {
if (!isModularize) {
var objects = [{ 'a': 30 }, { 'a': 50 }];
_.iteratee = getPropA;
strictEqual(_.sortedLastIndex(objects, { 'a': 40 }), 1);
strictEqual(_.sortedLastIndexBy(objects, { 'a': 40 }), 1);
_.iteratee = iteratee;
}
else {
@@ -14323,13 +14323,12 @@
QUnit.module('sortedIndex methods');
_.each(['sortedIndex', 'sortedLastIndex'], function(methodName) {
var array = [30, 50],
func = _[methodName],
isSortedIndex = methodName == 'sortedIndex',
objects = [{ 'x': 30 }, { 'x': 50 }];
var func = _[methodName],
isSortedIndex = methodName == 'sortedIndex';
test('`_.' + methodName + '` should return the insert index', 1, function() {
var values = [30, 40, 50],
var array = [30, 50],
values = [30, 40, 50],
expected = isSortedIndex ? [0, 1, 1] : [1, 1, 2];
var actual = _.map(values, function(value) {
@@ -14361,21 +14360,6 @@
deepEqual(actual, expected);
});
test('`_.' + methodName + '` should provide the correct `iteratee` arguments', 1, function() {
var args;
func(array, 40, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [40]);
});
test('`_.' + methodName + '` should work with a "_.property" style `iteratee`', 1, function() {
var actual = func(objects, { 'x': 40 }, 'x');
strictEqual(actual, 1);
});
test('`_.' + methodName + '` should align with `_.sortBy`', 10, function() {
var expected = [1, '2', {}, null, undefined, NaN, NaN];
@@ -14390,6 +14374,32 @@
strictEqual(func(expected, NaN), isSortedIndex ? 5 : 7);
});
});
});
/*--------------------------------------------------------------------------*/
QUnit.module('sortedIndexBy methods');
_.each(['sortedIndexBy', 'sortedLastIndexBy'], function(methodName) {
var func = _[methodName],
isSortedIndexBy = methodName == 'sortedIndexBy';
test('`_.' + methodName + '` should provide the correct `iteratee` arguments', 1, function() {
var args;
func([30, 50], 40, function() {
args || (args = slice.call(arguments));
});
deepEqual(args, [40]);
});
test('`_.' + methodName + '` should work with a "_.property" style `iteratee`', 1, function() {
var objects = [{ 'x': 30 }, { 'x': 50 }],
actual = func(objects, { 'x': 40 }, 'x');
strictEqual(actual, 1);
});
test('`_.' + methodName + '` should support arrays larger than `MAX_ARRAY_LENGTH / 2`', 12, function() {
_.each([Math.ceil(MAX_ARRAY_LENGTH / 2), MAX_ARRAY_LENGTH], function(length) {
@@ -14402,7 +14412,7 @@
var steps = 0,
actual = func(array, value, function(value) { steps++; return value; });
var expected = (isSortedIndex ? !_.isNaN(value) : _.isFinite(value))
var expected = (isSortedIndexBy ? !_.isNaN(value) : _.isFinite(value))
? 0
: Math.min(length, MAX_ARRAY_INDEX);
@@ -17347,7 +17357,7 @@
var args = arguments,
array = [1, 2, 3, 4, 5, 6];
test('should work with `arguments` objects', 27, function() {
test('should work with `arguments` objects', 28, function() {
function message(methodName) {
return '`_.' + methodName + '` should work with `arguments` objects';
}
@@ -17374,6 +17384,7 @@
deepEqual(_.lastIndexOf(args, 1), 0, message('lastIndexOf'));
deepEqual(_.rest(args, 4), [null, [3], null, 5], message('rest'));
deepEqual(_.sortedIndex(args, 6), 5, message('sortedIndex'));
deepEqual(_.sortedLastIndex(args, 6), 5, message('sortedLastIndex'));
deepEqual(_.take(args, 2), [1, null], message('take'));
deepEqual(_.takeRight(args, 1), [5], message('takeRight'));
deepEqual(_.takeRightWhile(args, _.identity), [5], message('takeRightWhile'));
@@ -17514,7 +17525,7 @@
var acceptFalsey = _.difference(allMethods, rejectFalsey);
test('should accept falsey arguments', 209, function() {
test('should accept falsey arguments', 211, function() {
var emptyArrays = _.map(falsey, _.constant([]));
_.each(acceptFalsey, function(methodName) {