forked from jashkenas/underscore
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor _.min and _.max (fix jashkenas#2688)
- Loading branch information
1 parent
fca3114
commit 8743fc0
Showing
11 changed files
with
216 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import identity from './identity.js'; | ||
import cb from './_cb.js'; | ||
import createReduce from './_createReduce.js'; | ||
|
||
// The general algorithm behind `_.min` and `_.max`. `compare` should return | ||
// `true` if its first argument is more extreme than (i.e., should be preferred | ||
// over) its second argument, `false` otherwise. `iteratee` and `context`, like | ||
// in other collection functions, let you map the actual values in `collection` | ||
// to the values to `compare`. `decide` is an optional customization point | ||
// which is only present for historical reasons; please don't use it, as it will | ||
// likely be removed in the future. | ||
export default function extremum(collection, compare, iteratee, context, decide) { | ||
decide || (decide = identity); | ||
// Detect use of a partially-applied `extremum` as an iteratee. | ||
if (typeof iteratee == 'number' && typeof collection[0] != 'object') { | ||
iteratee = null; | ||
} | ||
iteratee = cb(iteratee, context); | ||
// `extremum` is essentially a combined map+reduce with **two** accumulators: | ||
// an unmapped and a mapped version, corresponding to the same element. Our | ||
// `reduce` implementation only passes one accumulator on each iteration, | ||
// `iterResult` (the mapped version) so we close over the second accumulator, | ||
// `result` (the unmapped version). We define a custom `reduce` so that we can | ||
// map the first element to the initial accumulator and also set `result`. | ||
var result; | ||
var reduce = createReduce(1, function(value, key) { | ||
result = value; | ||
return iteratee(value, key, collection); | ||
}); | ||
var iterResult = reduce(collection, function(iterResult, value, key) { | ||
var iterValue = iteratee(value, key, collection); | ||
if (compare(iterValue, iterResult)) { | ||
result = value; | ||
return iterValue; | ||
} | ||
return iterResult; | ||
}); | ||
// `extremum` normally returns an unmapped element from `collection`. However, | ||
// `_.min` and `_.max` forcibly return a number even if there is no element | ||
// that maps to a numeric value. Passing both accumulators through `decide` | ||
// before returning enables this behavior. | ||
return decide(result, iterResult); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import isNaN from './isNaN.js'; | ||
|
||
// Internal comparison adapter for `_.min` and `_.max`. | ||
// Only considers numeric values as possible extremes. | ||
export function compareNumeric(compare) { | ||
return function(left, right) { | ||
if (right == null || +right !== +right) return true; | ||
return left != null && compare(+left, +right); | ||
} | ||
} | ||
|
||
// Internal `extremum` return value adapter for `_.min` and `_.max`. | ||
// Ensures that a number is returned even if no element of the | ||
// collection maps to a numeric value. | ||
export function decideNumeric(fallback) { | ||
return function(result, iterResult) { | ||
return isNaN(+iterResult) ? fallback : result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// A version of the `>` operator that can be passed around as a function. | ||
export default function greater(left, right) { | ||
return left > right; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
// A version of the `<` operator that can be passed around as a function. | ||
export default function less(left, right) { | ||
return left < right; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
// A version of the `<=` operator that can be passed around as a function. | ||
export default function lessEqual(left, right) { | ||
return left <= right; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,9 @@ | ||
import isArrayLike from './_isArrayLike.js'; | ||
import values from './values.js'; | ||
import cb from './_cb.js'; | ||
import each from './each.js'; | ||
import partial from './partial.js'; | ||
import _ from './underscore.js'; | ||
import extremum from './_extremum.js'; | ||
import { compareNumeric, decideNumeric } from './_forceNumericMinMax.js'; | ||
import greater from './_greater.js'; | ||
|
||
// Return the maximum element (or element-based computation). | ||
export default function max(obj, iteratee, context) { | ||
var result = -Infinity, lastComputed = -Infinity, | ||
value, computed; | ||
if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) { | ||
obj = isArrayLike(obj) ? obj : values(obj); | ||
for (var i = 0, length = obj.length; i < length; i++) { | ||
value = obj[i]; | ||
if (value != null && value > result) { | ||
result = value; | ||
} | ||
} | ||
} else { | ||
iteratee = cb(iteratee, context); | ||
each(obj, function(v, index, list) { | ||
computed = iteratee(v, index, list); | ||
if (computed > lastComputed || computed === -Infinity && result === -Infinity) { | ||
result = v; | ||
lastComputed = computed; | ||
} | ||
}); | ||
} | ||
return result; | ||
} | ||
// Forces a numeric result. | ||
export default partial(extremum, _, compareNumeric(greater), _, _, decideNumeric(-Infinity)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,9 @@ | ||
import isArrayLike from './_isArrayLike.js'; | ||
import values from './values.js'; | ||
import cb from './_cb.js'; | ||
import each from './each.js'; | ||
import partial from './partial.js'; | ||
import _ from './underscore.js'; | ||
import extremum from './_extremum.js'; | ||
import { compareNumeric, decideNumeric } from './_forceNumericMinMax.js'; | ||
import less from './_less.js'; | ||
|
||
// Return the minimum element (or element-based computation). | ||
export default function min(obj, iteratee, context) { | ||
var result = Infinity, lastComputed = Infinity, | ||
value, computed; | ||
if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) { | ||
obj = isArrayLike(obj) ? obj : values(obj); | ||
for (var i = 0, length = obj.length; i < length; i++) { | ||
value = obj[i]; | ||
if (value != null && value < result) { | ||
result = value; | ||
} | ||
} | ||
} else { | ||
iteratee = cb(iteratee, context); | ||
each(obj, function(v, index, list) { | ||
computed = iteratee(v, index, list); | ||
if (computed < lastComputed || computed === Infinity && result === Infinity) { | ||
result = v; | ||
lastComputed = computed; | ||
} | ||
}); | ||
} | ||
return result; | ||
} | ||
// Forces a numeric result. | ||
export default partial(extremum, _, compareNumeric(less), _, _, decideNumeric(Infinity)); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.