Clean way to test many conditions of different values? - javascript

So , I want to test many conditions, for different values... right now is just a bunch of if,else statements...
But it looks ugly im sure there must be a better way...
any ideas ?
Im thinking maybe with loops but or putting all vars in an array, but I cant figure out how..
Thx!
var dataObject = {}
if (newState.playerId){
dataObject["filter[player_id]"] = newState.playerId
}else{
dataObject["filter[player_id]"] = this.state.playerId
}
if (newState.pageLimit){
dataObject ["page[limit]"] = newState.pageLimit
}else{
dataObject["page[limit]"] = this.state.pageLimit
}
if (newState.timeFrom){
dataObject["time[from]"] = newState.timeFrom
}else{
dataObject["time[from]"] = this.state.timeFrom
}
if (newState.timeTo){
dataObject["time[to]"] = newState.timeTo
}else{
dataObject["time[to]"] = this.state.timeTo
}
if (newState.gameId){
dataObject["filter[game_id]"] = newState.gameId
}else{
dataObject["filter[game_id]"] = this.state.gameId
}
if (newState.customerId){
dataObject["filter[customer_id]"] = newState.customerId
}else{
dataObject["filter[customer_id]"] = this.state.customerId
}
if (newState.currency){
dataObject["filter[currency]"] = newState.currency
}else{
dataObject["filter[currency]"] = this.state.currency
}
if (newState.variant){
dataObject["filter[locale]"] = newState.locale
}else{
dataObject["filter[locale]"] = this.state.locale
}
if (newState.variant){
dataObject["filter[demo]"] = newState.demo
}else{
dataObject["filter[demo]"] = this.state.demo
}

Use the or (||) operator taking benefit of the short circuit evaluation, e.g.
dataObject["filter[player_id]"] = newState.playerId || this.state.playerId

Reducing your condition
First, you can use javascript's || operator and change:
if (newState.playerId){
dataObject["filter[player_id]"] = newState.playerId
}else{
dataObject["filter[player_id]"] = this.state.playerId
}
To the much reduced:
dataObject["filter[player_id]"] = newState.playerId || this.state.playerId;
DRY up your code
You can use an array of properties:
var propertyList = ["playerId", "pageLimit", "timeFrom" /* etc. */]
Because object properties can be referenced using square brackets you can loop through them like this:
propertyList.forEach(function(property){
dataObject[property] = newState[property] || this.state[property]
});
Disclaimer: This solution is not taking into account your embedded objects (like "filter") and the slight variations in your naming schemes (like "player_id" vs "playerId").
Three solutions occur to me:
Use consistent naming conventions
In other words in the dataObject that you build have the same naming pattern as your state object.
Use a helper function
Convert the names in your for loop using some kind of consistent pattern that changes playerId to player_id when those kinds of changes need to be done. (this will still not work if you plan to use "filter", "time" or "page".
Use objects/arrays (as in #ssube's solution)
You could also use an array or an object to translate your property names between objects. I won't give you an example - #ssube has done so already.

You have a recurring pattern here:
if (newState[srcField]) {
dataObject[destField] = newState[srcField]
} else {
dataObject[destField] = this.state[srcField]
}
Thanks to JS' handling of the OR operator, you can simplify that to:
dataObject[destField] = newState[srcField] || this.state[srcField];
Since you have the field names, you can set up a loop like:
var dataObject = {};
var fields = [
['playerId', 'filter[player_id]'],
['pageLimit', 'page[limit]']
];
fields.forEach(function (field) {
var src = field[0], dest = field[1];
dataObject[dest] = newState[src] || this.state[src];
});
and voila, the fields will be copied across with appropriate renaming.

var dataObject = {};
dataObject['filter[player_id]'] = newState.playerId || this.state.playerId;
dataObject['filter[game_id]'] = newState.gameId || this.state.gameId;
dataObject['filter[customer_id]'] = newState.customerId || this.state.customerId;
dataObject['filter[currency]'] = newState.currency || this.state.currency;
dataObject['filter[locale]'] = newState.variant ? newState.locale : this.state.locale;
dataObject['filter[demo]'] = newState.variant ? newState.demo: this.state.demo;
dataObject['page[limit]'] = newState.pageLimit || this.state.pageLimit;
dataObject['time[from]'] = newState.timeFrom || this.state.timeFrom;
dataObject['time[to]'] = newState.timeTo || this.state.timeTo;

Related

Combining two object in javascript and convert to String

I am hoping I am missing something obvious here, but I have tried for the past half day to set two variables to combine in a certain format in Javascript I can do it as a string, however I need it in a different format.
If I select on option from the check boxes {"GILLS":"7"} works fine however if two options are selected query1 should look like [{"GILLS":"1"},{"GILLS":"7"}]. I cannot use += to the variable as this kicks out an unexpected token error.
var Query1 = '';
var Query3 = '';
if ($('input[name=checkbox2a]:checked').length > 0) {
var Query3 = {"GILLS":"7"};
}
if ($('input[name=checkbox2b]:checked').length > 0) {
var Query3 = {"GILLS":"1"};
}
Try
var Query1 = [];
and in your function use
Query1.push({"GILLS":"1"})
So the change will be like below
var Query1 = [];
var Query3 = [];
if ($('input[name=checkbox2a]:checked').length > 0) {
Query3.push({"GILLS":"7"});
}
if ($('input[name=checkbox2b]:checked').length > 0) {
Query3.push({"GILLS":"1"});
}
then you can use join will give you string
Query3.join(", ")
Make an array out of Query3, if you still want a string at the end, use .join("").
var Query1 = '';
var Query3 = [];
if ($('input[name=checkbox2a]:checked').length > 0) {
Query3.push({"GILLS":"7"});
}
if ($('input[name=checkbox2b]:checked').length > 0) {
Query3.push({"GILLS":"1"});
}
Query3 = Query3.join(""); // back to string, if you so like
You're causing pain for yourself if you take the approach that sometimes the variable is of type Object ({"GILLS": "1" }) and sometimes it is of type List ([{"GILLS":"1"},{"GILLS":"7"}]).
Since it sometimes needs to be a list, make sure it's always a list. Even though sometimes it's a list of length 1. Then when you are reading that value, you never need conditional logic to deal with two potential types.
So, it's always a List:
var query3 = [];
... and then it's easy to manipulate:
if(...) {
query3.push( { "GILLS": "7" } );
}
if(...) {
query3.push( { "GILLS": "1" } );
}
At first, you are declaring same variable in a different scope. Don't do this unless uou know what you are doing. In other words use var keyword only once for a variable in a "space" when you are using this particular variable.
An advantage/disadvantage (chose one ;)) of javascript is that there is no type control. If You type var Query3 = ''; and then Query3 = {"GILLS":"7"}; interpreter will execute this without any complaints beside that there is high probability, that this is not exactly what you want to do ;). In this case it makes variable Query3 an empty string and then makes it an object.
In Your case, You want to have in result an array of objects. At first declare var Query3=[]; and then use a push method on it (Query3.push({});) to add elements.

How can I programmatically add to a variably-nested object?

I need a way to add an object into another object. Normally this is quite simple with just
obj[property] = {'name': bob, 'height': tall}
however the object in question is nested so the following would be required:
obj[prop1][prop2] = {'name': bob, 'height': tall}
The clincher though, is that the nesting is variable. That is that I don't know how deeply each new object will be nested before runtime.
Basically I will be generating a string that represents an object path like
"object.secondObj.thirdObj.fourthObj"
and then I need to set data inside the fourth object, but I can't use the bracket [] method because I don't know how many brackets are required beforehand. Is there a way to do this?
I am using jQuery as well, if that's necessary.
Sure, you can either use recursion, or simple iteration. I like recursion better. The following examples are meant to be proof-of-concept, and probably shouldn't be used in production.
var setDeepValue = function(obj, path, value) {
if (path.indexOf('.') === -1) {
obj[path] = value;
return;
}
var dotIndex = path.indexOf('.');
obj = obj[path.substr(0, dotIndex)];
return setDeepValue(obj, path.substr(dotIndex + 1), value);
};
But recursion isn't necessary, because in JavaScript you can just change references.
var objPath = 'secondObj.thirdobj.fourthObj';
var valueToAdd = 'woot';
var topLevelObj = {};
var attributes = objPath.split('.');
var curObj = topLevelObj;
for (var i = 0; i < attributes.length; i++) {
var attr = attributes[i];
if (typeof curObj[attr] === 'undefined') {
curObj[attr] = {};
}
curObj = curObj[attr];
if (i === (attributes.length - 1)) {
// We're at the end - set the value!
curObj['awesomeAttribute'] = valueToAdd;
}
}
Instead of generating a string...
var o="object";
//code
o+=".secondObj";
//code
o+=".thirdObj";
//code
o+=".fourthObj";
...you could do
var o=object;
//code
o=o.secondObj;
//code
o=o.thirdObj;
//code
o=o.fourthObj;
Then you can add data like this:
o.myprop='myvalue';
And object will be updated with the changes.
See it here: http://jsfiddle.net/rFuyG/

How to trim variables without erring if the var is empty?

I'm using the following for event tracking:
var dataTrack = e.split(','); // split by comma
if (dataTrack !== undefined) {
var action = dataTrack[0];
var values = {};
values[dataTrack[1]] = dataTrack[2];
mpq.track(action, values);
}
How can I trim dataTrack[0], dataTrack[1], dataTrack[2] in a way where if any of the dataTrack vars are empty it won't break? 1 & 2 are optional...
Thanks
A common idiom in JavaScript is to provide default values like so:
// default to the empty string
var dataTrack0 = dataTrack[0] || '',
dataTrack1 = dataTrack[1] || '',
dataTrack2 = dataTrack[2] || '';
...though I think a better solution, in this case, might be to check the length of the array.
You probably want to use the length property for the array.
var dataTrack = e.split(','); // split by comma
if (dataTrack !== undefined) {
var action = dataTrack[0];
var values = {};
if(dataTrack.length > 2) {
values[dataTrack[1]] = dataTrack[2];
}
mpq.track(action, values);
}
You could add extra validation to check that dataTrack[ 1] has a length > 0 if it is possible that someone would pass "value1,,value3".
Couldn't you just check to make sure they are not empty? Also you could use the ternary operator to have a default value if they are empty (i.e. action == undefined ? "Default" : datatrack[0];).
Replace
values[dataTrack[1]] = dataTrack[2];
with
if(dataTrack.length > 2){
values[dataTrack[1]] = dataTrack[2];
}

javascript function arguments

i want to pass an array to a function and work on it,but i am afraid ,error occurs saying board_pt is undefined. What is the problem? This is the code :
function set_boardPoint( board_pt,turn)
{
var no = board_pt.number-1;
board[no].row = board_pt.row;
board[no].col = board_pt.col;
board[no].x = board_pt.x;
board[no].y = board_pt.y;
board[no].value = turn;
board[no].number = board_pt.number;
}
board is a global array already defined
The problem is that board_pt have only 1 item, and js in these case know board_pt as object:
function set_boardPoint( board_pt,turn)
{
var no = board_pt.number-1;
if( board[no] != undefined )
{
board[no].row = board_pt.row;
board[no].col = board_pt.col;
board[no].x = board_pt.x;
board[no].y = board_pt.y;
board[no].value = turn;
board[no].number = board_pt.number;
}else
{
board.row = board_pt.row;
board.col = board_pt.col;
board.x = board_pt.x;
board.y = board_pt.y;
board.value = turn;
board.number = board_pt.number;
}
}
If board_pt is a real array, then it's unlikely that it has a property named number. Do you mean length?
From your comments, you have to define the previous_boardPoint as an array. To declare a array in javascript, you need to have - var previous_boardPoint = []; in your code. Also have each elements defined (if you have any) like previous_boardPoint[0] = val1; previous_boarPoint[1] = val2; etc.
If these things are not in your code, then in all likely possibilities, you will get board_pt as undefined in your function set_boardPoint.

Check if JSON keys/nodes exist

I'm using the Google Map API to retrieve city + state/region information from a postal code lookup. The issue is that in some cases a postal code lookup won't retrieve a city name. An example is 92625 (U.S).
var g = new GClientGeocoder();
g.setBaseCountryCode('US');
g.getLocations('92625', function(response){
if (response) {
var place = response.Placemark[0];
var state = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
var city = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName;
GLog.write("City = "+city+" : State/Region = "+state+" : Country = " + g.getBaseCountryCode());
}
});
In certain cases, as mentioned above, there won't be a city name in the result so there will be an undefined error for city, because the key Locality does not exist. This error prevents the rest of the script from running.
I was able to remedy it by...
if (place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality != null)
var city = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName;
else
var city = '';
...but this has me paranoid about a similar error for other keys. Eg: If AdministrativeArea is undefined the above IF statement would also cause an undefined error. So should I be checking to see if every Key/Node exists? Seems to be a messy approach because some of these keys are 5+ levels deep...is there an easier way to go about it, maybe some JQuery method I'm not familiar with?
Alternatively, you could make a function, that gives you defaults:
function valueOrDefault(val, def) {
if (def == undefined) def = "";
return val == undefined ? def : val;
}
And then use it like this:
var place = response.Placemark[0];
var state = valueOrDefault(place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName);
var city = valueOrDefault(place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName);
Personally, I think it's a little nicer to write, than p00ya's proposal, although it's a little hacky fiddling around in undefined objects ... one could maybe change it to this:
function drill(p, a) {
a = a.split(".");//add this
for (i in a) {
var key = a[i];
if (p[key] == null)
return '';
p = p[key];
}
return p;
}
var obj = {foo:{bar:{baz:"quux"}}};
var s = drill(obj, "foo.bar.baz"));//and you can use a simple property chain
You could use a function that "drills" down through all those nesting levels, defaulting to the empty string if it can't get that far.
function drill(p, a) {
for (i in a) {
var key = a[i];
if (p[key] == null)
return '';
p = p[key];
}
return p;
}
var obj = {foo:{bar:{baz:"quux"}}};
var s = drill(obj, ["foo", "bar", "baz"]));
I like back2dos' approach but I think it could be improved so as not to fail with ReferenceErrors:
function jPath(obj, a) {
a = a.split(".");
var p = obj||{};
for (var i in a) {
if (p === null || typeof p[a[i]] === 'undefined') return null;
p = p[a[i]];
}
return p;
}
// Tests
var a = {b:{c:'Hello World!'}, c:null};
console.log(jPath(a, 'b.c')); // Hello World
console.log(jPath(a, 'b.d')); // null
console.log(jPath(a, 'e.f.g')); // null
console.log(jPath(a, 'c')); // null
var z;
console.log(jPath(z, 'c')); // null
This kind of function is great for validating deep JSON return structures from AJAX services such as freebase or YQL.
You are looking at only the first result the geocoder gives you:
var place = response.Placemark[0];
getLocations() returns a list of several results. If the first one doesn't have it, one of the next few results almost certainly will.

Categories