Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript function arguments: positional -> map transition

I'm looking for a vanilla JavaScript solution.

Say I've got a function with the following header:

generateEmail(firstName, lastname, provider)

I need to run it like this:

generateEmail("John","Smith","gmail.com");

I would like to be able to call it with argument map instead of positional arguments, i.e.

generateEmail({
  "firstName":"John",
  "lastname": "Smith",
  "provider": "gmail.com"
});

And I'm looking for an already-written solution to do this in JavaScript, since I've got an unlimited number of functions such as generateEmail above to handle. Does such library exist?

I have seen https://github.com/kilianc/node-introspect which handles function introspection (returning function abstract parameter information). But the second part is missing - mapping map-call into positional-call.

Please tell me, whether such thing exists.


edit: if I didn't make myself clear: I don't want to modify the original positional-argument function. I get such functions from an external provider which may update his code. I'd rather prefer to have a wrapper that could call the original function beneath, and provide a map-argument API outside.

like image 949
ducin Avatar asked May 04 '15 12:05

ducin


2 Answers

Assuming you have access to introspect from node-introspect (which takes a function and returns an ordered list of arguments names), you can simply do:

function objArgsify(fn) {
    var argNames = introspect(fn);
    return function(obj) {
        return fn.apply(this,
                        argNames.map(function(a) { return obj[a]; });
    }
}

Call it by:

var generateEmailWithObjArgs = objArgsify(generateEmail);
generateEmailWithObjArgs({
  "firstName":"John",
  "lastname": "Smith",
  "provider": "gmail.com"
});

This accepts a function, reads its argument names, and then returns a wrapper function that accepts an object and uses the positional argument names to pull properties from the object-argument in the correct order.

This function uses the call-time object-argument as a map to transform the array ["firstName", "lastname", "provider"] into the array ["John", "Smith", "gmail.com"]. That array is then used with apply to invoke the postional-argument function.

like image 129
apsillers Avatar answered Oct 18 '22 20:10

apsillers


Without using any external libraries,

var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
  var fnStr = func.toString().replace(STRIP_COMMENTS, '');
  var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
  if(result === null)
     result = [];
  return result;
}

function call(method, object) {
    var params = getParamNames(method);
    var arrParams = [];
    for (var i = 0; i < params.length; i++) {
        arrParams.push(object[params[i]]);
    }
    return method.apply(arrParams);
}

Just call it with call(generateEmail, generateEmailObject).

like image 2
Evan Knowles Avatar answered Oct 18 '22 21:10

Evan Knowles