-
Notifications
You must be signed in to change notification settings - Fork 11
API
These return the array whose length is the smallest or greatest, respectively:
Array.shortest([1], [2, 2], [3, 3, 3]); // ↦ [1]
Array.longest([1], [2, 2], [3, 3, 3]); // ↦ [3, 3, 3]
Given arrays as parameters, returns an array whose length is that of the smallest one. Each element of the result is an array whose elements are the ith elements of each of the parameters (a tuple):
Array.zip([1, 1, 1, 1], [2, 2, 2], [3, 3, 3, 3, 3, 3, 3]); // ↦ [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
The same as Array.zip
, only taking as initial parameter a function, and instead of returning 'tuples', returns the result of applying said function to the ith elements of each parameter array:
function add(a, b, c) {
return a + b + c;
}
Array.zipWith(add, [1, 1, 1, 1], [2, 2, 2], [3, 3, 3, 3, 3, 3, 3]); ↦ [6, 6, 6]
Sugar for foo[ foo.length - 1 ]
.
Returns a new array containing this array broken up into subarrays of size length
.
[1, 2, 3, 4, 5].partition(2); // ↦ [[1, 2], [3, 4], [5]]
Returns an array comprised of the unique values of this array. The search
paramater is a function operating on an array bound to this
and taking one parameter, which should return -1
if the parameter is not in the array, and any other value otherwise.
If search
is not specified, the indexOf
function is used.
[2, 2, 2, 3, 3, 1, 2, 3, 4, 1, 3, 3, 4, 2, 1, 2, 3, 3, 4, 1].unique(); // ↦ [2, 3, 1, 4]
Creates a function whose invocation causes the invocation of func
. Its first parameter is bound to this
in said invocation, and the remaining are passed as arguments. This can be used to make code clearer, because it frees the writer from saying Lender.prototype.func.apply(foo, [args])
or Lender.prototype.call(foo, ...)
:
var slice = Function.liberate(Array.prototype.slice);
function variadic() {
var args = slice(arguments); // As opposed to slice.call(arguments);
// ...
}
Returns a new function which performs equivalent computation to that of func
, but that caches results, potentially improving speed. The keyGen
parameter is a function taking an array with the parameters received in a call to your function. If keyGen
is falsy, the default behavior is to serialize such array with JSON.stringify
. Example:
function fib(n) {
if (n == 1 || n == 0) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
fib(20); // 21891 function calls made
fib(25); // 242785 function calls made
fib(25); // 242785 function calls made
var fib = Function.memoize(fib);
fib(20); // 60 function calls made
fib(25); // 16 function calls made
fib(25); // 1 function call made
The improvements are often drastic, particularly for recursive functions, but remember that it does not always make sense to memoize. If the interesting part of your function are its side-effects, it makes no sense to memoize, since doing so merely decreases the return time by skipping repeated computation. Functions that are not [referentially transparent] (http://www.haskell.org/haskellwiki/Referential_transparency) - which do not depend only on their arguments - are often very bad candidates. Functions may be tied to factors such as time, system load, etc., which must not be overlooked when considering memoization.
The default behavior of keyGen
is undesired at times. Receiving objects as parameters requires clearly defining identity (remember - the order of object properties is unspecified).
Overall, [pure functions] (http://www.haskell.org/haskellwiki/Functional_programming#Purity) that are referentially transparent are great candidates. The Function.memoize
function introduces very little noise, and does not touch the original function's implementation at all. Applying it is only a matter of writing var foo = Function.memoize(foo);
, and removing memoization is as easy as deleting that statement.
Analogue of the .
operator in Haskell, and the ∘
operator in Mathematics.
For this function (f
), and given some other function g
, returns a new function f ∘ g
, such that (f ∘ g)(x)
is the same as f(g(x))
.
function square(x) { return x * x; }
function negate(x) { return -x; }
function decrease(x) { return x - 1; }
negate.compose(square).compose(decrease)(10); // ↦ -81
Together, the curry
and compose
functions bless the environment with strong reuse possibilities. New functions may be built out of existing ones with less verbosity than before, yet maintaining readability and clarity.
Returns a [curried] (http://www.haskell.org/haskellwiki/Currying) version of the given function.
Supposing f
is defined over n
parameters, f.curry()
is a function taking 1 parameter and returning a function that is the result of partially applying f
to the given parameter. This repeats until n
applications are met (so, for f.curry()
, n - 1
more times), at which stage the value and side effects given by a complete application of f
are reached.
It is also possible to partially apply f
to more than a single argument, and the same is true for all intermediate applications. In code:
function add3(one, two, three) {
return one + two + three;
}
add3.curry()(1)(2)(3); // ↦ 6
add3.curry()(1)(2, 3); // ↦ 6
add3.curry()(1, 2)(3); // ↦ 6
add3.curry()(1, 2, 3); // ↦ 6
Different semantics must be defined for the following scenario:
function product() {
return Array.prototype.slice.call(arguments).reduce(function (p, n) {
return p * n;
}, 1);
}
Since product
has variable arity, full application could never be reached according to the semantics previously described.
To remedy this, the optional depth
parameter can be given, so as to inform curry
of how many parameters are needed to completely define the computation:
var product3 = product.curry(3);
product3(1)(2)(3); // ↦ 6
Returns a function whose subsequent applications return the value of the original function applied to the last result:
function square(x) {
return x * x;
}
var sqs = square.iterate(2);
sqs(); // ↦ 4
sqs(); // ↦ 16
sqs(); // ↦ 256
sqs(); // ↦ 65536
Returns a function whose result is that of reducing this function's arguments using itself. Example:
var add = function (a, b) { return a + b },
sum = reducify(add);
add(1, 2); // ↦ 3
sum(1, 2, 3, 4); // ↦ 10
This could be described as left-associative composition. In other words, f.then(g)
is a function whose result is that of applying f
and then applying g
to the first result:
function square(x) { return x * x; }
function add10(x) { return x + 10; }
var magic = square.then(add10);
magic(10); // ↦ 110
ECMAScript performs floating point arithmetic to compute +
, -
, *
and /
. You should understand [why this is a problem] (http://dl.acm.org/citation.cfm?id=103163). Sinful gives the Math
object the add
, sub
, mul
, div
and intdiv
properties, whose values are all functions designed to perform fundamental arithmetic that is free from the problems of floating point representation (such as the fact that 0.1 + 0.2 !== 0.3). Examples:
Math.add(0.1, 0.2); // ↦ 0.3, instead of 0.30000000000000004
Math.sub(0.3, 0.2); // ↦ 0.1, instead of 0.09999999999999998
Math.mul(0.2, 0.1); // ↦ 0.02, instead of 0.020000000000000004
Math.div(0.3, 0.1); // ↦ 3, instead of 2.9999999999999996
Limits this number's value to the given bounds:
var nmb = 100;
nmb.limit(0, 50); // ↦ 50
nmb.limit(50, 150); // ↦ 100
nmb.limit(150, 200); // ↦ 150
Run func
this
many times, passing as argument to each call the current count (starting at 0):
(10).times(function (i) {
console.log(i);
}); // logs '0', then '1', then '2', ..., then '9'.
Returns a list going from this
to limit
, using stepper
to determine the intermediate values. If stepper
is ommited, function (x) { return x + 1; }
is used:
(1).to(15); // ↦ [1, 2, 3, ..., 15]
(15).to(1, function (x) { return x - 1; }); // ↦ [15, 14, 13, ..., 1]
(1).to(15, function (x) { return x * x; }); // ↦ [1, 4, 9, 16, ..., 225]
There are two preconditions:
-
stepper(i) != i
, for anyi
in the would-be resulting list; -
stepper
be monotonic.
Recursively mirrors this object's own property/values:
{foo: [{bar: "bar", ref: null}]}.deepCopy(); // ↦ {foo: [{bar: "bar", ref: null}]} (different ones)
[[1, 2, [3, 4]], [3, [5, 6]]].deepCopy(); // ↦ [[1, 2, [3, 4]], [3, [5, 6]]] (different ones)
The same as mapOwn
, only no array is returned.
It is common practice in JavaScript to chain function calls, taking advantage of the manageable object-oriented syntax the language provides:
var result = obj.foo(...).
bar(...).
baz(...);
We will call the thing being returned after each of the chain's calls the ball.
This pattern is problematic when, at some point in between two of the chain's calls, some computation must be performed that depends both on the state of the ball and on that of the universe. This becomes worse when that computation is not meant to belong to the finished product - for instance, when debugging.
The intercept
function that sinful provides to all objects solves this problem:
var result = obj.foo(...).
bar(...).
intercept(function (ball) {
console.log(ball.someStateHolder);
}).
baz(...);
It is easy to understand what intercept
does. It applies the func
parameter to this
(the ball), and returns this
(the ball). It does not return the result of applying func
to the ball.
The ball is then delivered to the next function down the chain.
Returns an array whose elements are the results of calling func
on every string that is a property name of this
. At every iteration, func
receives the name of a property, its index within the complete array of properties and the array of properties itself, in that order. The optional parameter self
is bound to this
in func
, for every call. Directly equivalent to saying Object.getOwnPropertyNames(obj).map(func, self)
.
({ foo: 10, bar: false }).mapOwn(function (prop, i, props) {
return prop.toUpperCase();
}) // ↦ ["FOO", "BAR"]
It is often necessary to prevent from using undefined
when procuring the value of some object's property:
// Obtain obj in some way
foo(obj.prop); // undefined makes no sense for foo.
The solution is trivial (if statements, the ternary operator or ||). However, the problem is more annoying as structures become larger:
var obj = {
boo: {
bar: {
baz: 10
}
}
};
foo(obj.boo.bar.baz); // undefined makes no sense for foo.
// Additionally, if obj.boo or obj.boo.bar
// are undefined, this is an error.
The solution here would be to employ a series of checks on every property access. This is ugly and not always useful. Object.prototype.maybe
solves this problem neatly:
foo(obj.maybe('boo.bar.baz', 'pidgeon'));
Above, if obj.boo
, obj.boo.bar
or obj.boo.bar.baz
are undefined
, foo
receives 'pidgeon'
.
Not providing the second argument would cause that to yield undefined
, though there would be no danger of producing runtime errors.
An array of strings, each representing the property to be evaluated at each step, may also be provided -- example: `obj.maybe(['boo', 'bar', 'baz'], 'pidgeon')'.
Returns a string composed of the interpolated values for the given keys. Key/value pairs are given by a parameter object:
'{name} is {age}.'.interp({
name: 'George',
age: 30
}); // ↦ 'George is 30.'
Returns a string made of times
repetitions of this string, separated by sep
:
'echo\n'.repeat(3); // ↦ 'echo\necho\necho'
'echo'.repeat(3, '\n'); // ↦ 'echo\necho\necho'
Self-explanatory:
'abcdef'.reverse(); // ↦ 'fedcba'
Returns a string whose length is not greater than maxLen
. If this string's length is greater than maxLen
, the truncation suffix
is appended to its end. If maxLen
is falsy, 50
is used. If suffix
is falsy, '...'
is used.
'1234567'.truncate(5); // ↦ '12...'