From 97a7d25f0f4ff33f6f60c2629b8ac0b67fc984e8 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Fri, 13 Mar 2015 10:28:36 -0400 Subject: [PATCH] Add _.sortedLastIndex Closes https://github.com/jashkenas/underscore/issues/1882. --- test/arrays.js | 30 ++++++++++++++++++++++++++++-- underscore.js | 29 +++++++++++++++++++---------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 3f39ed90a..61905a1b1 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -112,9 +112,9 @@ }); test('sortedIndex', function() { - var numbers = [10, 20, 30, 40, 50], num = 35; + var numbers = [10, 20, 30, 30, 30, 40, 50], num = 35; var indexForNum = _.sortedIndex(numbers, num); - equal(indexForNum, 3, '35 should be inserted at index 3'); + equal(indexForNum, 5, '35 should be inserted at index 5'); var indexFor30 = _.sortedIndex(numbers, 30); equal(indexFor30, 2, '30 should be inserted at index 2'); @@ -137,6 +137,32 @@ equal(_.sortedIndex(array, 2147483648), 2147483648, 'should work with large indexes'); }); + test('sortedLastIndex', function() { + var numbers = [10, 20, 30, 30, 30, 40, 50], num = 35; + var indexForNum = _.sortedLastIndex(numbers, num); + equal(indexForNum, 5, '35 should be inserted at index 5'); + + var indexFor30 = _.sortedLastIndex(numbers, 30); + equal(indexFor30, 5, '30 should be inserted at index 5'); + + var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}]; + var iterator = function(obj){ return obj.x; }; + strictEqual(_.sortedLastIndex(objects, {x: 25}, iterator), 2); + strictEqual(_.sortedLastIndex(objects, {x: 35}, 'x'), 3); + + var context = {1: 2, 2: 3, 3: 4}; + iterator = function(obj){ return this[obj]; }; + strictEqual(_.sortedLastIndex([1, 3], 2, iterator, context), 1); + + var values = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647]; + var array = Array(Math.pow(2, 32) - 1); + var length = values.length; + while (length--) { + array[values[length]] = values[length]; + } + equal(_.sortedLastIndex(array, 2147483648), 2147483648, 'should work with large indexes'); + }); + test('uniq', function() { var list = [1, 2, 1, 3, 1, 4]; deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array'); diff --git a/underscore.js b/underscore.js index 374aec8e7..b9dcbd7bf 100644 --- a/underscore.js +++ b/underscore.js @@ -655,16 +655,25 @@ // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iteratee, context) { - iteratee = cb(iteratee, context, 1); - var value = iteratee(obj); - var low = 0, high = array.length; - while (low < high) { - var mid = Math.floor((low + high) / 2); - if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; - } - return low; - }; + function createSortedIndexFinder(lessThan) { + return function(array, obj, iteratee, context) { + iteratee = cb(iteratee, context, 1); + var value = iteratee(obj); + var low = 0, high = array.length; + while (low < high) { + var mid = Math.floor((low + high) / 2); + var computed = iteratee(array[mid]); + if ((lessThan && computed < value) || (!lessThan && computed <= value)) { + low = mid + 1; + } else { + high = mid; + } + } + return low; + }; + } + _.sortedIndex = createSortedIndexFinder(true); + _.sortedLastIndex = createSortedIndexFinder(false); // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See