silentmatt
6/19/2009 - 6:43 PM

Functions for dealing with function parameters

Functions for dealing with function parameters

/*
    Get an array of parameter names for a function.

    Example:

        js> function f(a, b, c) { return a + b * c; }
        js> JSON.stringify(Function.parameters(f));
        ["a","b","c"]
*/
Function.parameters = function(f) {
    var m = /function[^\(]*\(([^\)]*)\)/.exec(f.toString());
    if (!m) {
        throw new TypeError("Invalid function in parameters");
    }

    var params = m[1].split(',');
    for (var i = 0; i < params.length; i++) {
        // trim any spaces
        params[i] = params[i].replace(/^\s*|\s*$/g, '');
    }
    return params;
};

/*
    Create a new function that accepts its parameters in an object instead of
    its positional parameters. An optional array of names can be provided
    instead of using the parameters defined in the function (to rename
    parameters, or since some functions might use parameters that aren't named).

    Examples:

        js> function f(a, b, c) { return a + b * c; }
        js> f(1, 2, 3);
        7
        js> var f1 = Function.withNamedParameters(f);
        js> f1({ a:1, b:2, c:3 });
        7
        js> // Call with an array (like f.apply)
        js> var f2 = Function.withNamedParameters(f, [0, 1, 2]);
        js> f2([1, 2, 3]);
        7
*/
Function.withNamedParameters = function(f, params) {
    // XXX: should this force f to be a function, or should anything that passes
    // the parameters RegExp with an apply function work?
    params = params || Function.parameters(f);
    return function(args) {
        var argsArray = new Array(params.length);
        for (var i = 0; i < params.length; i++) {
            argsArray[i] = args[params[i]];
        }
        return f.apply(this, argsArray);
    };
};

/*
    Create a new function that accepts its positional parameters as objects
    mapping parameter names to values. Each parameter array maps ascending
    arguments to properties in an object. With a single parameter array, this is
    the opposite of Function.withNamedParameters.

    Examples:

        js> function f(p1, p2) { return { x: p1.x + p2.x, y: p1.y + p2.y }; }
        js> var f1 = Function.withPositionalParameters(f, ["x","y"], ["x","y"]);
        js> JSON.stringify(f1(1, 2, 3, 4));
        {"x":4,"y":6}

        js> function f(a, b, c) { return a + b * c; }
        js> var f1 = Function.withNamedParameters(f);
        js> var f2 = Function.withPositionalParameters(f1, ["a", "b", "c"]);
        js> f2(1, 2, 3); // f2 is equivalent to (but slower than) f
        7

        js> // Reorder parameters
        js> var f3 = Function.withPositionalParameters(f1, ["c", "b", "a"]);
        js> f3(3, 2, 1);
        7
*/
Function.withPositionalParameters = function(f, params /*, ... */) {
    params = Array.prototype.slice.call(arguments, 1);

    return function() {
        var args = [];
        var argumentsIndex = 0;

        for (var i = 0; i < params.length; i++) {
            var arg = {};
            var paramList = params[i];
            args.push(arg);
            for (var p = 0; p < paramList.length; p++) {
                arg[paramList[p]] = arguments[argumentsIndex];
                ++argumentsIndex;
            }
        }

        return f.apply(this, args);
    };
};