Lightweight JavaScript Module for Optional/Default Parameters-Collection of common programming errors

Is there a JavaScript module that makes it easy to map arguments to parameters? Here is how I envision it working:

var arguments = assignArguments(arguments, ‘p1’, ‘p2’, [{‘p3’: 0}, ‘p4’], {‘p5’: ‘unknown’});

Within a function, you would call this to generate an object that associated a parameter with an argument. Parameters defined within an array or inline object would be considered optional, where inline objects would permit assigning default values. All other parameters are considered “required”.

Here are some sample inputs/outpus:

foo(1): { p1: 1, p3: 0, p5: 'unknown' } // no p2 (aka undefined) 
foo(1, 2): { p1: 1, p2: 2, p3: 0, p5: 'unknown' } 
foo(1, 2, 3): { p1: 1, p2: 2, p3: 0, p4: 3, p5: 'unknown' } 
foo(1, 2, 3, 4): { p1: 1, p2: 2, p3: 3, p4: 4, p5: 'unknown' } 
foo(1, 2, 3, 4, 5): { p1: 1, p2: 2, p3: 3, p4: 4, p5: 5 }

I am hoping a library like this already exists. This logic gets repeated a lot and I want to eliminate it if possible.

Is anyone aware of a library like this? If not, can someone send me down the right path for implementing one?

  1. I went ahead and thought of a way of doing this myself. It involves parsing an object graph made up of strings, arrays and objects. It’s quite long, but it works well.

    function assignArguments(values, expression) {              
        // determine how many arguments are needed for each parameter
        // grab all of the defaults, too
        var needed = 1;
        var argsNeeded = {};
        var defaults = {};
        var queue = [expression];               
        for (var queueIndex = 0; queueIndex < queue.length; ++queueIndex) {
            var node = queue[queueIndex];
            if (typeof node === 'string') {
                // the node is a required parameter
                argsNeeded[node] = needed;
                ++needed;
            } else if (node instanceof Array) {
                // the node is a list of parameters
                for (var index = 0; index !== node.length; ++index) {
                    queue.push(node[index]);
                }
            } else {
                // the node is a list of optional parameters with defaults
                // make sure there isn't more than one parameter
                var count = 0;
                for (var key in node) {
                    if (node.hasOwnProperty(key)) {
                        if (count !== 0) {
                            throw new Error('A default parameter list had more than one value.');
                        }
                        defaults[key] = node[key];
                        queue.push(key);
                        ++count;
                    }
                }
            }
        }
    
        // determine the order that the parameters appear
        var parameters = [];
        var stack = [expression];
        while (stack.length > 0) {
            var node = stack.pop();
            if (typeof node === 'string') {
                // the node is a required parameter
                parameters.push(node);
            } else if (node instanceof Array) {
                // the node is a list of parameters
                var index = node.length;
                while (index !== 0) {
                    --index;
                    stack.push(node[index]);
                }
            } else {
                // the node is an optional parameter with defaults
                // we'll check for multiple parameters
                var count = 0;
                for (var key in node) {
                    if (node.hasOwnProperty(key)) {
                        if (count > 0) {
                            throw new Error('A default parameter list had more than one value.');
                        }
                        stack.push(key);
                        ++count;
                    }
                }
            }
        }
    
        var result = {};
        var valueIndex = 0;
        for (var index = 0; index !== parameters.length; ++index) {
            var parameter = parameters[index];
            var needed = argsNeeded[parameter];
            if (needed > values.length) {
                if (parameter in defaults) {
                    result[parameter] = defaults[parameter];
                }
            } else {
                result[parameter] = values[valueIndex];
                ++valueIndex;
            }
        }
    
        return result;
    }
    

    And here is an example using it.

    function foo() {
        var params = assignArguments(arguments, ['p1', 'p2', [{p3:0}, 'p4'], {p5: 'unknown'}]);
        return params;
    }
    
    console.log(foo(1));
    console.log(foo(1, 2));
    console.log(foo(1, 2, 3));
    console.log(foo(1, 2, 3, 4));
    console.log(foo(1, 2, 3, 4, 5));
    

Originally posted 2013-11-09 21:05:29.