-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Functional style #2908
base: master
Are you sure you want to change the base?
Functional style #2908
Changes from all commits
c22e1e9
ce15d38
f0e9bc3
0b51eae
f4e0f71
11bdeab
3bc4c50
ea55aeb
5092fb5
008990f
bd14ef1
38129be
be5b8a2
adfb2f8
e842fd5
5d73fb3
3753fe7
d7393b4
18fd848
296be9e
60319af
645eafe
aac1042
431b84e
5781d89
81a7a77
b4ab797
5bc6d47
eef39cd
b63e425
2fac273
9624b1b
3120fd2
3a326ad
1f3562a
cba898c
230fee1
a0ef35a
8764f04
bc0e942
65c16db
8cfa13f
8edde7b
19ba06e
5f46a25
dfd6d6b
13fca95
457a96d
7daecbe
ce3f0ed
c654128
fca3114
8743fc0
bd49157
5f72f90
c71244a
e108783
e2a9a83
448d2e8
5165f1f
9bf9dc3
64e62ef
aaa0fcb
01f324e
6a0ebd8
7fee0d5
f60a3ef
ba7df7a
1cb9003
674ed39
b7637b6
d5b8d74
b99a079
9de1ceb
20e88e5
d82eadc
640332b
341ad30
eaba5b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import getLength from './_getLength.js'; | ||
|
||
// Iteratively cut `array` in half to figure out the index at which `obj` should | ||
// be inserted so as to maintain the order defined by `compare`. | ||
export default function binarySearch(array, obj, iteratee, compare) { | ||
var value = iteratee(obj); | ||
var low = 0, high = getLength(array); | ||
while (low < high) { | ||
var mid = Math.floor((low + high) / 2); | ||
if (compare(iteratee(array[mid]), value)) low = mid + 1; else high = mid; | ||
} | ||
return low; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// Internal function that returns a bound version of the | ||
// passed-in callback, used in `_.iteratee`. | ||
export default function bindCb(func, context) { | ||
if (context === void 0) return func; | ||
return function() { | ||
return func.apply(context, arguments); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// In Firefox, `Function.prototype.call` is faster than | ||
// `Function.prototype.apply`. In the optimized variant of | ||
// `bindCb` below, we exploit the fact that no Underscore | ||
// function passes more than four arguments to a callback. | ||
// **NOT general enough for use outside of Underscore.** | ||
export default function bindCb4(func, context) { | ||
if (context === void 0) return func; | ||
return function(a1, a2, a3, a4) { | ||
return func.call(context, a1, a2, a3, a4); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
import isFunction from './isFunction.js'; | ||
import bindCb4 from './_bindCb4.js'; | ||
import _ from './underscore.js'; | ||
import baseIteratee from './_baseIteratee.js'; | ||
import iteratee from './iteratee.js'; | ||
import './iteratee.js'; | ||
|
||
// The function we call internally to generate a callback. It invokes | ||
// `_.iteratee` if overridden, otherwise `baseIteratee`. | ||
export default function cb(value, context, argCount) { | ||
if (_.iteratee !== iteratee) return _.iteratee(value, context); | ||
return baseIteratee(value, context, argCount); | ||
// The function we call internally to generate a callback: a wrapper | ||
// of `_.iteratee`, which uses `bindCb4` instead of `bindCb` for | ||
// function iteratees. It also saves some bytes in the minified code. | ||
export default function cb(value, context) { | ||
if (isFunction(value)) return bindCb4(value, context); | ||
return _.iteratee(value, context); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import _ from './underscore.js'; | ||
import chain from './chain.js'; | ||
|
||
// Helper function to continue chaining intermediate results. | ||
export default function chainResult(instance, obj) { | ||
return instance._chain ? _(obj).chain() : obj; | ||
return instance._chain ? chain(obj) : obj; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,28 @@ | ||
import getLength from './_getLength.js'; | ||
import { slice } from './_setup.js'; | ||
import binarySearch from './_binarySearch.js'; | ||
import identity from './identity'; | ||
import less from './_less.js'; | ||
import lessEqual from './_lessEqual.js'; | ||
import isNaN from './isNaN.js'; | ||
import linearSearch from './_linearSearch.js'; | ||
|
||
// Internal function to generate the `_.indexOf` and `_.lastIndexOf` functions. | ||
export default function createIndexFinder(dir, predicateFind, sortedIndex) { | ||
return function(array, item, idx) { | ||
var i = 0, length = getLength(array); | ||
if (typeof idx == 'number') { | ||
if (dir > 0) { | ||
i = idx >= 0 ? idx : Math.max(idx + length, i); | ||
} else { | ||
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; | ||
} | ||
} else if (sortedIndex && idx && length) { | ||
idx = sortedIndex(array, item); | ||
return array[idx] === item ? idx : -1; | ||
// Internal function to generate the `indexOf` and `lastIndexOf` functions. | ||
export default function createIndexFinder(dir) { | ||
var forward = dir > 0; | ||
var compare = forward ? less : lessEqual; | ||
// `controlArg` may be either a number indicating the first index to start | ||
// searching at, or a boolean indicating whether the array is sorted by native | ||
// operator `<`. | ||
return function(array, item, controlArg) { | ||
var start; | ||
if (getLength(array) < 1) return -1; | ||
if (typeof controlArg == 'number') { | ||
start = controlArg; | ||
} else if (controlArg && forward) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
start = binarySearch(array, item, identity, compare); | ||
return array[start] === item ? start : -1; | ||
} | ||
if (item !== item) { | ||
idx = predicateFind(slice.call(array, i, length), isNaN); | ||
return idx >= 0 ? idx + i : -1; | ||
} | ||
for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { | ||
if (array[idx] === item) return idx; | ||
} | ||
return -1; | ||
var predicate = item !== item ? isNaN : { value: item }; | ||
return linearSearch(array, predicate, dir, start); | ||
}; | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,28 @@ | ||
import isArrayLike from './_isArrayLike.js'; | ||
import keys from './keys.js'; | ||
import optimizeCb from './_optimizeCb.js'; | ||
import bindCb4 from './_bindCb4.js'; | ||
|
||
// Internal helper to create a reducing function, iterating left or right. | ||
export default function createReduce(dir) { | ||
// Create a reducing function iterating in the same way as `loop` (e.g. | ||
// `_.find`). | ||
export default function createReduce(loop) { | ||
// Wrap code that reassigns argument variables in a separate function than | ||
// the one that accesses `arguments.length` to avoid a perf hit. (#1991) | ||
var reducer = function(obj, iteratee, memo, initial) { | ||
var _keys = !isArrayLike(obj) && keys(obj), | ||
length = (_keys || obj).length, | ||
index = dir > 0 ? 0 : length - 1; | ||
function reducer(obj, iteratee, memo, initial) { | ||
if (!initial) { | ||
memo = obj[_keys ? _keys[index] : index]; | ||
index += dir; | ||
} | ||
for (; index >= 0 && index < length; index += dir) { | ||
var currentKey = _keys ? _keys[index] : index; | ||
memo = iteratee(memo, obj[currentKey], currentKey, obj); | ||
// Make the `iteratee` change identity temporarily so that it only sets | ||
// the `memo` on the first iteration. | ||
var actualIteratee = iteratee; | ||
iteratee = function(memo, value) { | ||
iteratee = actualIteratee; | ||
return value; | ||
} | ||
} | ||
loop(obj, function(value, key, obj) { | ||
memo = iteratee(memo, value, key, obj); | ||
}); | ||
return memo; | ||
}; | ||
} | ||
|
||
return function(obj, iteratee, memo, context) { | ||
var initial = arguments.length >= 3; | ||
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); | ||
return reducer(obj, bindCb4(iteratee, context), memo, initial); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import identity from './identity.js'; | ||
import cb from './_cb.js'; | ||
import find from './find.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`. | ||
export default function extremum(collection, compare, iteratee, context, decide) { | ||
// `extremum` is essentially a combined map+reduce with **two** accumulators: | ||
// `result` and `iterResult`, respectively the unmapped and the mapped version | ||
// corresponding to the same element. | ||
var result, iterResult; | ||
iteratee = cb(iteratee, context); | ||
var first = true; | ||
find(collection, function(value, key) { | ||
var iterValue = iteratee(value, key, collection); | ||
if (first || compare(iterValue, iterResult)) { | ||
result = value; | ||
iterResult = iterValue; | ||
first = false; | ||
} | ||
}); | ||
Comment on lines
+14
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This loop represents the "slow path" that I mentioned in the opening post. The fast path is currently still duplicated in |
||
// `extremum` would normally return `result`. 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. | ||
// `decide` is an optional customization point which is only present for the | ||
// above historical reason; please don't use it, as it will likely be removed | ||
// in the future. | ||
return (decide || identity)(result, iterResult); | ||
} |
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; | ||
} |
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 less(left, right) { | ||
return left < right; | ||
} |
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 lessEqual(left, right) { | ||
return left <= right; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import getLength from './_getLength.js'; | ||
import isFunction from './isFunction.js'; | ||
|
||
// Internal function for linearly iterating over arrays. | ||
export default function linearSearch(array, predicate, dir, start) { | ||
var target, length = getLength(array); | ||
dir || (dir = 1); | ||
start = ( | ||
start == null ? (dir > 0 ? 0 : length - 1) : | ||
start < 0 ? (dir > 0 ? Math.max(0, start + length) : start + length) : | ||
dir > 0 ? start : Math.min(start, length - 1) | ||
); | ||
// As a special case, in order to elide the `predicate` invocation on every | ||
// loop iteration, we allow the caller to pass a value that should be found by | ||
// strict equality comparison. This is somewhat like a rudimentary iteratee | ||
// shorthand. It is used in `_.indexof` and `_.lastIndexOf`. | ||
if (!isFunction(predicate)) { | ||
target = predicate && predicate.value; | ||
predicate = false; | ||
} | ||
for (; start >= 0 && start < length; start += dir) { | ||
if ( | ||
predicate ? predicate(array[start], start, array) : | ||
array[start] === target | ||
) return start; | ||
} | ||
return -1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a more general version of
_.sortedIndex
, usingcompare
instead of builtin operator<
._.sortedIndex
now callsbinarySearch
internally.