Skip to content
guipn edited this page Nov 7, 2012 · 7 revisions

Arrays

Array.shortest(...), Array.longest(...)

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]

Array.zip(...)

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]]

Array.zipWith(f, ...)

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]

Array.prototype.last()

Sugar for foo[ foo.length - 1 ].

Array.prototype.partition(length)

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]]

Array.prototype.unique([search])

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]

Functions

Function.liberate(func)

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);

    // ...

}

Function.memoize(func, [keyGen])

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.

Function.prototype.compose(g)

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.

Function.prototype.curry([depth])

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

Function.prototype.iterate(target)

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

Function.prototype.reducerify()

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

Function.prototype.then(f)

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

Math

Basic Arithmetic

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

Numbers

Number.prototype.limit(lower, upper)

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

Number.prototype.times(func)

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'.

Number.prototype.to(limit, [stepper])

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 any i in the would-be resulting list;
  • stepper be monotonic.

Objects

Object.prototype.deepCopy()

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)

Object.prototype.forEachOwn(func, [self])

The same as mapOwn, only no array is returned.

Object.prototype.intercept(func)

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.

Object.prototype.mapOwn(func, [self])

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"]

Object.prototype.maybe(propertyPath, otherwise)

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')'.

Strings

String.prototype.interp(kv)

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.'

String.prototype.repeat(times, [sep])

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'

String.prototype.reverse()

Self-explanatory:

'abcdef'.reverse(); // ↦ 'fedcba'

String.prototype.truncate(maxLen, [suffix])

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...'