Shorter code for this JavaScript "if" statement - javascript

Is there a short way to write the following using either JavaScript or jQuery?
if (this.id==="a" || this.id==="b" || this.id==="c" || this.id==="d")

How about this?
if ( this.id in { "a":1, "b":1, "c":1, "d":1 } ) {
...
}
... or this?
if("abcd".indexOf(this.id) > -1) {
...
}

if ( ['a','b','c','d'].indexOf( this.id ) >= 0 ) { ... }
or
if ( this.id in {'a':0,'b':0,'c':0,'d':0} ) { ... }

One possibility is a switch statement.
switch(this.id){case"a":case"b":case"c":case"d":
//do something
}

You can try the following code. Especially when you have more than four test values.
if (/^[abcdef]$/.test(this.id)) {
...
}

The inline anonymous hash (d in o) performance was misrepresented in the tests as originally written, because the hash wasn't inline in the test.
Oddly enough, the true inline hash case, compared to the predefined hash case, is much slower in Firefox 4, but 50% faster in Chrome 12.
But a more important point is that d in o misses the point of a hash—that you don't have to iterate to find things.
Two lines, but still pretty short, and by far the fastest:
var o = {a:1,b:1,c:1,d:1};
if(o[this.id]){...}

Related

Variable in JSON Path - JavaScript

I already searched for similar issues but I didn't find anything that could help me yet.
I'm trying to reach a picture path (using JSON format) depending on the material type of the picked element. Actually, my code is built like this:
if (globalData.Material.Mat_type == "OSCILLOSCOPE") {
var picture = (globalData.Material.Oscilloscope.picture);
}
if (globalData.Material.Mat_type == "ALIMENTATION") {
var picture = (globalData.Material.Alim.picture);
}
But not optimized at all, so Im trying to make it this way :
var mat_type = (globalData.Material.Mat_type);
var picture = (globalData.Material[mat_type].picture);
But it doesn't work... Got some exception:
TypeError : globalData.Material[mat_type] is undefined.
I already tried a lot of things, have you got any idea? Thanks!
I outlined the issue with character case in the comment under the question, so presumably adjusting value of globalData.Material.Mat_type could do the trick:
var mat_type =
globalData.Material.Mat_type.charAt(0).toUpperCase() +
globalData.Material.Mat_type.substr(1).toLowerCase();
I can also see that this general rule may not be applicable in all cases. If it's not a typo, it won't work for the second case where Mat_type == "ALIMENTATION", because then you try to access Alim property of Material instead of Alimentation. In this case you could access property by prefix:
function pictureOf(material) {
if (!material || !String(material.Mat_type)) {
return null;
}
let mat_type = String(material.Mat_type).toUpperCase();
for (var propertyName in material) {
if (mat_type.startsWith(propertyName.toUpperCase())) {
return material[propertyName].picture || null;
}
}
return null;
}
console.log(pictureOf({
Mat_type: "OSCILLOSCOPE",
Oscilloscope: {
picture: "picture of oscilloscope"
}
}));
console.log(pictureOf({
Mat_type: "ALIMENTATION",
Alim: {
picture: "picture of alimentation"
}
}));
But this kind of approach can be error prone, if multiple properties share the same prefix. There's also a hidden issue with case-insensitive prefix matching in case you use some special unicode characters in property names. Lastly this method is not efficient, because it has to iterate over all properties of the object (worst case scenario). It can be replaced with much safer property mapping:
const matTypeMapping = {
"ALIMENTATION": "Alim"
};
function pictureOf(material) {
if (!material || !String(material.Mat_type)) {
return null;
}
let matType = String(material.Mat_type);
// find property mapping or apply general rule, if mapping not defined
let propertyName = matTypeMapping[matType] ||
matType.charAt(0).toUpperCase() + matType.substr(1).toLowerCase();
return material[propertyName].picture || null;
}
console.log(pictureOf({
Mat_type: "OSCILLOSCOPE",
Oscilloscope: {
picture: "picture of oscilloscope"
}
}));
console.log(pictureOf({
Mat_type: "ALIMENTATION",
Alim: {
picture: "picture of alimentation"
}
}));
NB: To avoid headaches, maybe you should prefer strict equality operator over loose equality operator.
Problem Solved
Peter Wolf was right ! It was a case-sensitive issue
I actually don't know how to promote his comment, sorry for this..
Anyway, thank you guys !
var mat_type = (globalData.Material.Mat_type);
if(mat_type!==undefined)
var picture = (globalData.Material[mat_type].picture)
Just do an existential check before accessing the value, for keys that may not be present.

jQuery return multiple function

so, i have the following js:
function RHL(a,b,c)
{
return rx.removeClass(a).addClass(b);
return rhpfc.html(parseInt( rhpfc.html() ) -1 );
}
I am having a bit of difficult time with the formatting and syntax.
How do I combine both lines under one return. Also, I want to have two options: -1 or +1. So, I thought I would make - or + as c.
what kind of bracket do I need? (ie. 'c'1)
function RHL(a,b,c){
return [
rx.removeClass(a).addClass(b),
rhpfc.html(parseInt( rhpfc.html() ) -1 )
];
}
then you will need to use the index 0 or 1 to use the return value..
var rx = RHL(a,b,c)[0];
or
var rhpfc = RHL(a,b,c)[1];

lack of identity between jQuery selector and jQuery variable?

I'm running into a maddening problem where I set a variable to point to a jQuery selector, such as: var foobar=jQuery(this); I then pass this variable to a function to be worked on. Let's simplify a little and say the function looks like this:
function SetFieldValue (selector) {
selector.val('test');
console.log ( selector );
console.log ( jQuery('#' + selector.attr('id')) );
}
In this situation if you assume that:
the selector is always a form element (and therefore val() is a valid operation)
the selector does resolve to a single dom element which has an 'id' attribute
You would then expect the two console.log statements to output the same result, right? Well I'm running into a situation where this condition only happens about 90% of the time.
In order to give more context I've created a short screencast demonstrating the problem:
SCREENCAST LINK
For reference purposes, here's the actual SetFieldValue code that is shown in the screencast:
function SetFieldValue ( domObject, value ) {
// as a safety function, check if a string representation of the domObject was passed in and convert it to a jQuery object if it was
if ( jQuery.type(domObject) === "string") {
console.log ("Value passed into SetFieldValue was a string representation so converting to jQuery object");
domObject = jQuery(domObject);
}
if ( jQuery.inArray (domObject.prop('tagName').toLowerCase(),['input' , 'select' , 'textarea']) >= 0 ) {
console.log ("setting to value attribute: " + value);
if ( domObject.hasAttr('id') ) {
domObject.val(value);
//jQuery('#' + domObject.attr('id')).val(value);
} else {
domObject.attr('value',value);
}
console.log ("Using jQuery ID it is set to: " + jQuery('#' + domObject.attr('id')).val() );
console.log ("Using jQuery selector variable it is set to: " + domObject.val() );
} else {
console.log ("setting to html attribute");
domObject.html( value );
}
return domObject;
}
Lets examine the code a bit.
First assigning back to a parameter is not a good practice adding a var at the start of your function would be a lot better, as scope can be lost.
//Suggestion change parameter to domItem
var domObject
Your missing an error handler for when the parameter is not String.
when identifying the type use
<VARNAME>.constructor.toString().match(/function (\w*)/)[1] === "<TYPE>"
It's more efficient and handles custom types.
No need for all the logic in assignment of value attribute. Any dom Object can be made to have a value attribute. also not sure why you are setting the val versus the value.
domObject.attr('value',value);
It is at this point that I can see your code could really use some documentation to help explain purpose
If you are explicitly only wanting to set value on Input fields and set value as innerhtml on non input fields then yes the logic would be needed but could be simplified to ... as the value doesn't need to be detected to overwritten.
if (jQuery.inArray (domObject.prop('tagName').toLowerCase(), ['input' , 'select' , 'textarea']) >= 0) {
domObject.attr('value',value);
} else {
domObject.html( value );
}
No Idea why you are returning the domObject out.
So a quick rewrite without the return and keeping most of the logic adding error handling results in
/*jslint sloppy: true*/
/*global jQuery*/
function SetFieldValue(domString, value) {
// as a safety function, check if a string representation of the domObjects was passed in and convert it to a jQuery object if it was
var domObjects, index;
//errorhandling
if (domString === undefined || domString === null) {
throw {error : "domString must have a value."};
}
if (domString.constructor.toString().match(/function (\w*)/)[1] !== "string") {
if (domString.constructor.toString().match(/function (\w*)/)[1].match(/HTML[a-zA-Z]*Element/) === null) {
throw {error : "domString expected to be String or domObjects"};
}
} else {
if (jQuery(domString).length === 0) {
throw {error : "domString does not resolve to a detectable domObjects."};
}
}
//errorhandling
//action
if (domString.constructor.toString().match(/function (\w*)/)[1].match(/HTML[a-zA-Z]*Element/)) {
//made as an array to normalize as jQuery returns an array allows code to be simplified
domObjects = [domString];
} else {
domObjects = jQuery(domString);
}
//given that domObjects are an array need to step through the array
for (index = domObjects.length - 1; index >= 0; index -= 1) {
if (
jQuery.inArray(
domObjects[index].tagName.toLowerCase(),
['input', 'select', 'textarea']
) >= 0
) {
if (domObjects[index].hasAttr('id')) {
domObjects[index].val(value);
} else {
domObjects[index].attr('value', value);
}
} else {
domObjects[index].html(value);
}
}
}
The above passes JSLint
I know I didn't provide enough context for people to really dig into this problem but I have in the end solved it. What was the issue? Well it was #Kobi who first asked is the DOM element's ID unique ... to which I happily reported it was. And it had been but in fact that WAS the problem. Jesus. It's always the obvious things that you then go onto overlook that get you in trouble.
Anyway, thanks for your patience and help.

javascript "ors" - can they be combined into an array?

wondering if I can make my javascript more efficient.
I have a var JSP = "the jsp's name"
And I have statements in a javascript validation file:
if(( JSP == "test.html" ) || ( JSP == "test1.html") || ( JSP == "test2.html" )) then blah blah blah.
Is there a more efficient way to do this?
If you know that JSP contains a string, it's slightly more efficient to use === rather than ==. Also note that you don't need all those parens:
if (JSP === "test.html" || JSP === "test1.html" || JSP === "test2.html") {
// do something
}
You could also use a regular expression:
if (/^test[12]?\.html$/.test(JSP)) {
// do something
}
...but it depends what you mean by "efficient." The series of === will be very efficient at runtime.
Separately, you could use a switch:
switch (JSP) {
case "test.html":
case "test1.html":
case "test2.html":
// Do it
break;
}
...but I wouldn't call it more efficient.
I definitely would not put the options in an array, because searching through the array will not be efficient. But you can use a map:
var pages = {
"test.html": true,
"test1.html": true,
"test2.html": true
};
...and then this test:
if (pages[JSP] === true) {
// do something
}
...which results in a fairly efficient property lookup. That's only reasonable if you create the object once and reuse it.
(You might have people say "Or just use if (pages[JSP]) { ... }. But that fails if JSP happens to contain "toString" or "valueOf" or any of several other inherited properties blank objects get from Object.prototype. It's fine if you're certain it won't have any of those values, though.)
You could create an object with those keys:
var theThings = { "test.html": true, "test1.html": true, "test2.html": true };
if (theThings[JSP]) { /* whatever */ }
If there are only three or four, it might not be worth it, but if there are dozens it'd definitely be faster, especially if the test gets made several times.
edit — wow I'm crying a little inside here, guys. Property name lookups are going to be way faster than linear searches through an array.
var arr = ['test.html', 'test1.html', 'test2.html'];
if (arr.indexOf(JSP)) != -1) {
alert("found it!");
}
relevant docs here.
if( JSP in {"test.html":0, "test2.html":0, "test3.html":0} ) {
...
}
It doesn't get any closer to SQL's IN( 1, 2, 3) than this in javascript :-)
if (["test.html", "test1.html", "test2.html"].indexOf(JSP) > -1)
For browsers that don't support indexOf on arrays, MDC suggests short piece of code that adds missing functionality.
Probably not more efficient, but you got cleaner ways to do it. You could for instance use a switch-case like this:
switch(JSP) {
case 'test.html':
case 'test1.html':
case 'test2.html':
blablabla; break;
}
Or you could create an array out of the urls and see if your string is in the array like this
var arr = [
'test.html', 'test1.html',
'test2.html'
];
if(arr.indexOf(JSP) != -1) { blablabla; }
The last one will not work in all browsers.
A way of doing it in jQuery is to use the inArray method, e.g.:
if ($.inArray(JSP, ["test.html", "test1.html", "test2.html"]) > -1) {
// your code
}
The inArray method works in a similar manner to String.indexOf so -1 is returned if no match.
Use a regular expression?
if (/^test\d?\.html$/.test(JSP)) { ... }
I can't promise that will be more efficient though, just tidier code-wise.
Or if you're already using jQuery, you could use jQuery.inArray():
var names = ['test.html', 'test2.html', 'test3.html'];
if ($.inArray(JSP, names)) { ... }
Or with underscore.js
var names = ['test.html', 'test2.html', 'test3.html'];
if (_.indexOf(names, JSP) !== -1) { ... }

How to shorten long repeating path's and combine statements

for a website I use tabs with 9 different looks, these are used in different situations. It all works fine but I use a lot of functions with path's and statements that could be a lot shorter I think. An example:
function tabAon() {
tabAvoor=tabAvoor+1;
tabDrawer=tabDrawer+1;
tabBvoor=0;
tabCvoor=0;
window.document.getElementById("tabA").style.textDecoration="none";
window.document.getElementById("tabB").style.backgroundPosition="-87px -228px";
window.document.getElementById("tabB").style.color="#898f92";
window.document.getElementById("tabC").style.backgroundPosition="-190px -228px";
window.document.getElementById("tabC").style.color="#898f92";
window.document.getElementById("tabA").style.cursor="pointer";
if (tabDrawer==0) {
window.document.getElementById("header").MozTransform="translateY(0px)";
window.document.getElementById("header").msTransform="translateY(0px)";
window.document.getElementById("header").OTransform="translateY(0px)";
window.document.getElementById("header").WebkitTransform="translateY(0px)";
window.document.getElementById("header").transform="translateY(0px)";
window.document.getElementById("tabA").style.color="#898f92";
window.document.getElementById("tabA").style.backgroundPosition="0px 0px";
}
if (tabDrawer==1) {
window.document.getElementById("header").style.MozTransform="translateY(36px)";
window.document.getElementById("header").style.msTransform="translateY(36px)";
window.document.getElementById("header").style.OTransform="translateY(36px)";
window.document.getElementById("header").style.WebkitTransform="translateY(36px)";
window.document.getElementById("header").style.transform="translateY(36px)";
window.document.getElementById("tabAout").style.visibility="visible";
window.document.getElementById("tabBout").style.visibility="hidden";
window.document.getElementById("tabCout").style.visibility="hidden";
window.document.getElementById("tabA").style.color="#000000";
window.document.getElementById("tabA").style.backgroundPosition="0px -114px";
}
if (tabAvoor==2) tabAvoor=1;
if (tabDrawer==2 && tabBvoor==0 && tabCvoor==0) tabDrawer=0;
if (tabDrawer==2 && tabBvoor==1 || tabCvoor==1) tabDrawer=1;
}
What about using a JS framework? E.g: jQuery
Example:
$('#tabBout').show();
Instead of window.document.getElementById("tabAout").style.visibility="visible";
You can shorten your code a little by defining a variable for your different IDs:
var tabA = window.document.getElementById("tabA");
// window.document.getElementById("tabA").style.textDecoration="none";
tabA.style.textDecoration="none";
For a start, you should try using some version of jQuery's super cool "$()" function in order to shorten all those "window.document.getElementById" , ie.:
function $(id)
{
return window.document.getElementById(id);
}
Then you can just do this:
$("header").style.MozTransform="translateY(36px)";
(btw: there are many other nice things you can do with $(), ie. return entire arrays of objects... the version I've wrote here is just an example)
And you can reduce your code a lot by using some functions, ie.:
function set_transforms(id,pos)
{
$(id).MozTransform="translateY("+pos+"px)";
$(id).msTransform="translateY("+pos+"px)";
$(id).OTransform="translateY("+pos+"px)";
$(id).WebkitTransform="translateY("+pos+"px)";
$(id).transform="translateY("+pos+"px)";
}
So you can replace all this:
window.document.getElementById("header").style.MozTransform="translateY(36px)";
window.document.getElementById("header").style.msTransform="translateY(36px)";
window.document.getElementById("header").style.OTransform="translateY(36px)";
window.document.getElementById("header").style.WebkitTransform="translateY(36px)";
window.document.getElementById("header").style.transform="translateY(36px)";
with just this:
set_transforms("header",36);
Hope it helps!

Categories

Resources