How to generalize a function? - javascript

I am using the below function, I am trying to generalize the function without using hardcoded values. How i can achieve it?
function ChangeIDToString(strCondition,id)
{
if (strCondition.indexOf("AssignedTo") > -1)
return GetUserName(id)
else if (strCondition.indexOf("ClaimStatusId") > -1)
return GetClaimStatus(id)
else if (strCondition.indexOf("ClaimTypeId") > -1)
return GetClaimType(id);
else
return id;
}

It would be nice to see what your functions do.
But in your functions, you can declare:
var j={"id":"return of your function string here"};
JSON.stringify(j); // '{"id":"return of your function string here"}'

You could create an object of functions, and then call a function named by strCondition using bracket notation and dynamic property name. Something like this:
var objOfFunctions = {
GetUserName: function (id) {...},
GetClaimStatus: function (id) {...},
GetClaimType: function (id) {...}
};
function ChangeIDToString(strCondition,id) {
if (!objOfFunctions.hasOwnProperty(strCondition)) {
return id;
}
return objOfFunctions[strCondition](id);
}
In a case you already have a lot of calls to the functions in objOfFunctios somewhere else in your code, you can populate that object with references too.
A live demo using references in objOfFunctions at jsFiddle.
EDIT
After discovered that you've asked this same question before, it looks like my original answer still hardcodes too much. (Your comment to void's similar answer.)
The goal can still be achieved by passing a function reference instead of a string to ChangeIDToString. Though you will lose the ability to check, which function will be called. Like this:
function ChangeIDToString(refCondition,id) {
if (typeof refCondition !== 'function') {
return id;
}
return refCondition(id);
}
// Invoke example
console.log(ChangeIDToString(GetClaimType, 'some_ID'));
A demo at jsFiddle.
If the string can't be replaced with a reference, your last resort is to use eval(), but it's strongly recommended not to do so.
An eval demo at jsFiddle.

You weren't clear as to where/how you wanted JSON to fit into your solution, but if you are saying that there will definitely be one of those 3 strings passed into the function and which function gets called from there is based on which string is passed in, then you could leverage the fact that all arguments are optional in JavaScript. As long as your function only gets called with 2 arguments (id and one of the others), this would do it:
function ChangeIDToString(id, user, claimStatusId, claimTypeId)
{
if (user !== null)
return GetUserName(id)
else if (claimStatusId !== null)
return GetClaimStatus(id)
else if (claimTypeId !== null)
return GetClaimType(id);
else
return id;
}
https://jsfiddle.net/m271cpum/ for more complete example.

Related

Prevent multiple DOM searches in jquery

I created a function to return me a jquery element.
function GetDialogButton() {
return $('a.dialog');
};
This was done as the same element was used within multiple other functions. I thought it best if it was obtained from a single place, therefore making it easier to change in future should the atribute name change.
I would like to improve this getter so that it does not perform a search everytime when called multiple times within a single page load.
How can I do this? do I cache it? or perhaps there is no need as this is optimised out?
You can create a cache variable, but it will pollute the global namespace again
var dialogButton;
function GetDialogButton() {
if(dialogButton){
return dialogButton;
}
dialogButton = $('a.dialog');
return dialogButton;
};
Creating a global cache variable is not necessary. You can do it without adding a variable to the global scope. Something like this would do:
var GetDialogButton = (function() {
var set;
return function() {
if (set === undefined) {
set = $('a.dialog');
}
return set;
};
}());
Well, you could lazy-load it.
var $dialogButton = null;
function GetDialogButton() {
if($dialogButton == null)
$dialogButton = $('a.dialog');
return $dialogButton
};
Another alternative, if you expect there to only be one dialog button you could give the element an id and then the act of searching for it will be more efficient
<a id="dialogButton">...</a>
$('#dialogButton')... // nice and quick
You could keep the global namespace clean by;
function GetDialogButton() {
if (typeof GetDialogButton.element === 'undefined' ) {
GetDialogButton.element = $("a.dialog");
}
return GetDialogButton.element;
};

How do I verify if several attributes are in a JSON structure within JavaScript?

I am creating a module that takes in several complicated JSON files and would like some code to give the user feedback if certain elements are absent.
Below is the way I am doing it now, but I cannot help to think there must be a cleaner, less hacky way.
var _und = require("underscore");
//this function takes a list of required attributes and ensures they are present
var check_req_attr = function(config, req_attr, callback) {
var config_attr = Object.keys(config);
var absent_attr = _und.difference(req_attr, config_attr); //slightly hacky code that checks to ensure config has correct vars
if (absent_attr.length !== 0) {
throw Error("missing following attributes from config:" + absent_attr);
} else {
callback();
};
};
It just feels...dirty. If there is no real elegant way to do it, I would be open to critiques on my code. Thanks!
Parse the JSON to JS.
var data = JSON.parse(theJson);
Use something like:
function hasKey(obj, key) {
return typeof obj[key] !== 'undefined';
};
function hasKeys(obj, keys) {
for (var i = 1, len = keys.length; i < len; i++) {
if (!hasKey(obj, keys[i])) {
return false;
};
};
return true;
};
Now you can simply do:
if (hasKeys(data, ["firstKey", "secondKey", "thirdKey"]) {
console.log("valid");
};
This should be the way to do it, using every and has:
if (_und.every(req_attr, function(attr) {
return _und.has(config, attr);
}))
throw new Error();
In a native environment, you would just use the in operator:
req_attr.every(function(attr){ return attr in config; })
I think your solution is actually quite elegant! No need for an anonymous function, and the loop (which must happen at some point, obviously) neatly abstracted away with difference.
Two suggestions:
I'd give the function a synchronous signature. No callback argument. There can't be any reason to go async if you honor the function signature (i.e. basing your answer on config and req_attr only).
I'd change the function to return the missing properties (attributes is wrong term). You could also add a requireProperties function that uses this "check" function that would throw if a property was missing. This allows for different kind of uses.
Why don't you try with something like:
obj = JSON.parse(json);
and then check
if(obj.YourProperty == undefined){
//do something..
}
Hope i understood your question.. It should work with complicated JSON files too.. Good luck ;)
You could also use the in operator (requiredAttr in obj):
function objHasAllRequiredAttrs(obj, attrNames) {
return attrNames.reduce(function(memo, attrName) {
return memo && (attrName in obj);
}, true);
}
objHasAllRequiredAttrs({foo:1}, ['foo']); // => true
objHasAllRequiredAttrs({bar:1}, ['foo']); // => false

Trouble referencing variable in Collections.where method within render function

I have run into some trouble with a piece of backbone code. The code below relates to a render function. I can retrieve all the models. My trouble arises when I try to use the "Collections.where" method at line marked number #1. As you can see, I have passed an object literal into the render function but for some reason I am unable to reference it within the customers.where method on line #1. When I give this method a literal number like 45 it works. Is there some way around this so I can pass the variable reference in?
Thanks alot
render: function(options) {
var that = this;
if (options.id) {
var customers = new Customers();
customers.fetch({
success: function (customers) {
/* #1 --> */ var musketeers = customers.where({musketeerId: options.id});
console.log(musketeers.length) //doesn't work as options.id is failing on last line
var template = _.template($('#customer-list-template').html(), {
customers: customers.models
});
that.$el.html(template);
console.log(customers.models);
}
});
} else {
var template = _.template($('#customer-list-template').html(), {});
that.$el.html(template);
}
}
Although it isn't explicitly documented, Collection#where uses strict equality (===) when searching. From the fine source code:
where: function(attrs, first) {
if (_.isEmpty(attrs)) return first ? void 0 : [];
return this[first ? 'find' : 'filter'](function(model) {
for (var key in attrs) {
if (attrs[key] !== model.get(key)) return false;
}
return true;
});
},
note the attrs[key] !== model.get(key) inside the callback function, that won't consider 10 (a probable id value) and '10' (a probable search value extracted from an <input>) to be a match. That means that:
customers.where({musketeerId: 10});
might find something whereas:
customers.where({musketeerId: '10'});
won't.
You can get around this sort of thing with parseInt:
// Way off where you extract values from the `<input>`...
options.id = parseInt($input.val(), 10);

javascript: dynamic call of nested function

in an existing implementation (can't change the structure much), i'm trying to call a function which is nested inside another function:
function outer(innerFunction, obj) {
//TODO: call innerFunction here, passing obj as first parameter
function inner1(obj) {
alert(obj.key);
}
}
outer('inner1', {key:'value'});
jsfiddle is here: http://jsfiddle.net/tbyyw/
i've alreay thought about using eval(), but i don't know how to pass an object - and they say 'eval is evil' ;)
another solution i've come up with is checking the innerFunction string, but this means i have to know which inner functions exist (besides, adding new functions would mean having to write extra checks then):
if(innerFunction == 'inner1') inner1(obj);
so is there another way without changing the overall implementation?
Without changing the overall structure eval appears to be the only option:
function outer(funcName, obj) {
var func = eval(funcName);
func(obj);
function inner1(obj) {
alert(obj.key);
}
}
There's nothing particularly "evil" about eval as long as you have full control over the code, but if you want, you can insert an additional security check:
if (funcName.match(/\W/))
throw "invalid function name!";
var func = eval(funcName);
This will raise an exception if someone tries to pass anything else than a simple identifier, i.e. a function name.
Is this what you wanted?
function outer(innerFunction, obj) {
var fn = {
inner1: function (obj) {
alert(obj.key);
}
};
fn[innerFunction](obj);
}
outer('inner1', {key:'value'});
http://jsfiddle.net/tbyyw/1/
A simple switch statement would be least intrusive. Or is the function name completely dynamic?
function outer(innerFunction, obj) {
switch (innerFunction) {
case "inner1": inner1(obj); break;
}
function inner1(obj) {
alert(obj.key);
}
}
outer('inner1', {key:'value'});
​

Why does this function return as undefined?

String.prototype.parse = function(f) {
alert(this.replace(f, ""));
};
var a = "Hello World";
parse.apply(a, ["Hello"]);
Is the code correct?
No, that’s not correct. The function is defined as String.prototype.parse, so it is not available as parse (in fact, parse is undefined).
You could run it like the following:
String.prototype.parse.apply(a, ["Hello"]);
But actually, the reason why you add the function to the prototype of String is that you extend String objects with that function. So you actually should just run the function like this:
a.parse("Hello");
edit:
Oh, and in response to your question title “Why does this function return as undefined?”: The function doesn’t return anything, because you don’t tell the function to return anything. You could for example define it like this to return the replaced string (instead of alerting it):
String.prototype.parse = function(f) {
return this.replace(f, "");
};
And then you could alert the return value of the function:
alert(a.parse("Hello"));
There is no such variable parse defined in your code sample. If you really want to apply the function later on, you should do this:
// Capture function as a local variable first
var parse = function(f) { alert(this.replace(f, "")); };
String.prototype.parse = parse;

Categories