javascript hashmap one line modify or create - javascript

I want to know if there is a clean way to modify a value in a hashmap or create it if it doesn't exist without doing an if block. Example of what i'm currently doing
let dict = {}
if(dict['key']){
dict['key'] += 1
} else {
dict['key'] = 1
}
Want to know if there is a cleaner way to do what I did above.

Use dot notation, because the property isn't dynamic, and alternate dict.key with 0 before adding 1.
dict.key = (dict.key || 0) + 1;

Related

Push string to array if the array you want to push from is undefined

I am not a coder, I am messing around with some JavaScript as part of modding a game, so bear with me. This game supports es5/everything Chromium 28 supported.
I had code which pushed a string to an array from a variable, and when the variable was undefined a fixed string was pushed instead:
slotsArray.push({
landing_policy: ai.landing_policy || 'no_restriction'
});
The setup changed such that where ai.landing_policy was set it would contain multiple values, so it become an array. When it wasn't set only a single entry was required.
The same code does not appear to work where an array is in place:
for (var i = 0; i < count; i++) {
slotsArray.push({
landing_policy: ai.landing_policy[i] || 'no_restriction'
});
}
An error is produced because it's trying to check a value from a variable that hasn't been defined. I expected that to cause it to use the fixed value, but apparently that's not what happens, it just fails.
I've changed my approach to the code seen below in full:
if (Array.isArray(ai.landing_policy)) {
for (var i = 0; i < count; i++) {
slotsArray.push({
landing_policy: ai.landing_policy[i]
});
}
}
else {
slotsArray.push({
landing_policy: ai.landing_policy || 'no_restriction'
});
}
This code works, but what I'm looking to understand is whether this was the best solution? The old method felt elegant, while the new one looks a little clumsy.
You can use the ternary operator(? :).
It will return the second value if the first is true, and the third otherwise.
I've used array instanceof Array instead of Array.isArray(array) to support ES5.
var isArray = ai.landing_policy instanceof Array
for (var i = 0; i < (isArray ? count : 1); i++) {
slotsArray.push({
landing_policy: isArray ? ai.landing_policy[i] : ai.landing_policy || 'no_restriction'
});
}
Elegant solution not always converse to the most readable/desirable. I would probably do something like:
const formattedPolicy = ai.landing_policy.map(policy => policy || 'no_restriction');
slotsArray = [...formattedPolicy ];
Course this has to imply that the ai.landing_policy is always an array. If you need to double check first you could also do:
const formattedPollicy = ai.landing_policy.constructor === Array
? ai.landing_policy.map(policy => policy || 'no_restriction');
: [ai.landing_policy]
Looks like an elegant or short imho but your code is way more readable.

Is there a way to prevent undefined javascript variables from displaying 'undefined' when inserted into html?

I am appending text which is stored in a javascript variable into a div element. The issue is that the depending on the situation there may or may not be text stored in that variable. If there is not I end up with the text 'undefined' where the valid text would have been in the div.
so as an example:
htmlelement.innerhtml = '<h2>'+array.object.title+
'</h2><p>'+array.object.textField1+
'</p><p>'+array.object.textField2+
'</p><p>'+array.object.textfield3+'</p>';
This shows up in a function which will run for each object in the array. Not all of the objects have content in all 3 text fields.
So is there an easy way to prevent 'undefined from being printed?
Right now I have this before the previous line:
if (!array.object.textfield1) {
array.object.textfield1 = ' ';
}
if (!array.object.textfield2) {
array.object.textfield2 = ' ';
}
if (!array.object.textfield3) {
array.object.textfield3 = ' ';
}
But this is not a practical solution if there are a lot of variables that need to be checked.
Can you use the logical operator || ?
array.object.textField1||''
Note: Please do take care of values like 0 or any other falsy values .
Use "The New Idiot" answer this is here just fro an extra method.
The other answer is better because it molds the check into the logic ( a good thing!) and is better for performance.
with that said REGEX!!
htmlelement.innerText = htmlelement.innerText.replace('undefined', '');
check each array item to see if its undefined with the **typeof** operator.
for each array item if the **typeof** is **undefined** you can do eather 2 things:
1. set to default
2. remove with splice()
example:
function cleanArray(theArray){
for(i=0;i < theArray.length;i++){
if(typeof theArray[i] == "undefined"){
theArray[i]="";//OR SPLICE IT OU WITH splice()
}
}
}
//NOW CALL THIS FUNCTION EVERYTIME PASSING IT THE ARRAY
cleanArray(arrayOfItems);
no simple way around this, you need to plan your design accordingly
"The New Idiot" answer is pretty good if you only have a few. If you have a more complicated object that you want to sort out, one option would be to iterate over the properties and set them to an empty string if they are undefined. e.g.
var o = {
t1: undefined,
t2: "hey"
};
for (prop in o) {
if (o.hasOwnProperty(prop) && typeof o[prop] === "undefined") {
o[prop] = "";
}
}
jsFiddle: http://jsfiddle.net/Ca6xn/

whats wrong with this ternary operator?

I have an object menuNames which should maintain a list of menu items. If menuNames already has the slug, increment the value, if it doesnt contain the slug, set the value equal to 1. I'm doing this to track unique names. I want to end up with something like:
menuNames: {
home: 1,
products: 10,
contact: 1
}
this doesnt work (this would be contained in a loop going through each slug):
menuNames[slug] = (menuNames.hasOwnProperty(slug) ? menuNames[slug]++ : 1);
//this sets every value to 1
but this does work (this would be contained in a loop going through each slug):
if(menuNames.hasOwnProperty(slug)) {
menuNames[slug]++;
} else {
menuNames[slug] = 1;
}
menuNames[slug]++ increments the value, but also returns the original value.
You are doing menuNames[slug] =, so the value is set back to the original value after being incremented.
To fix it, just simply do:
menuNames[slug] = (menuNames.hasOwnProperty(slug) ? menuNames[slug]+1 : 1);
Or:
(menuNames.hasOwnProperty(slug) ? menuNames[slug]++ : menuNames[slug] = 1);
I guess it could work like this:
menuNames[slug] = (menuNames.hasOwnProperty(slug) ? ++menuNames[slug] : 1);
As the other answers say the problem is in the post increment.
Another way to write it is:
menuNames[slug] += (some_bool ? 1 : 0);
++ is very sensitive to bugs. Try to write it as a += statement.
if menuNames[slug] can be undefined, write it as:
menuNames[slug] = 0;
if (some_bool) {
menuNames[slug] += 1;
}
This is (in my opinion) the clearest way to write an initialization/counter loop.
If you like one-liners you'll cringe, but if you like bug free code you'll be happy to see this.

Avoiding having to write the same word over and over again

I'm very new to javascript so this question might sound stupid. But what is the correct syntax of replacing certain words inside variables and functions. For example, I have this function:
function posTelegram(p){
var data = telegramData;
$("#hotspotTelegram").css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$("#hotspotTelegram").hide()
} else {
$("#hotspotTelegram").show()
}
};
There is the word "telegram" repeating a lot and every time I make a new hotspot I'm manually inserting the word to replace "telegram" in each line. What would be a smarter way of writing that code so that I only need to write "telegram" once?
Group similar / related data in to data structures instead of having a variable for each bit.
Cache results of calling jQuery
Use an argument
function posGeneral(p, word){
// Don't have a variable for each of these, make them properties of an object
var data = generalDataThing[word].data;
// Don't search the DOM for the same thing over and over, use a variable
var hotspot = $("#hotspot" + word);
hotspot.css("left", generalDataThing[word].xpos[p] +"px");
if (p < data[0] || p > data[1]) {
hotspot.hide()
} else {
hotspot.show()
}
};
You can't always avoid this kind of repetition (this is general to all programing languages).
Sometimes, you can make generic functions or generic classes, for example a class which would embed all your data :
Thing = function(key, xpos) {
this.$element = $('#hotspot'+key);
this.xpos = xpos;
};
Thing.prototype.pos = function (p, data) {
this.$element.css("left", this.xpos[p] +"px");
if (p < this.data[0] || p > this.data[1]) {
this.$element.hide()
} else {
this.$element.show()
}
};
And we could imagine that this could be called like this :
var telegramThing = new Thing('telegram', xposTelegram);
...
telegramThing.pos(p, data);
But it's really hard to make a more concrete proposition without more information regarding your exact problem.
I recommend you read a little about OOP and javascript, as it may help you make complex programs more clear, simple, and easier to maintain.
For example, using a Thing class here would enable
not defining more than once the "#hotspotTelegram" string in your code
reusing the logic and avoid making the same code with another thing than "telegram"
not having the Thing logic in your main application logic (usually in another Thing.js file)
But don't abstract too much, it would have the opposite effects. And if you don't use objects, try to keep meaningful variable names.
var t = "Telegram";
var $_tg = $('#hotspotTelegram');
$_tg.css("left", "xpos"+t[p] + "px"); // not sure about this line, lol
$_tg.hide();
$_tg.show();
etc.
you can create a selector as variable, something like this
function posTelegram(p){
var data = telegramData;
var $sel = $("#hotspotTelegram");
$sel.css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$sel.hide()
} else {
$sel.show()
}
};

Finding in a predefined set of text options

Say, I want to see if a DOM element is a block. I can write it in three ways, depending on my mood:
// first way
if (el.currentStyle.display == "block" || el.currentStyle.display == "inline-block" || el.currentStyle.display == "table-cell")
// second way
var blocks = {"block": 1, "inline-block": 1, "table-cell": 1};
if (el.currentStyle.display in blocks)//
// third way
if (el.currentStyle.display.match(/block|inline-block|table-cell/))
I have mixed feeling about all of them. First is too verbose once I have more than one option. Second contains those arbitrary values in the object (where I put 1s this time). Third looks like overkill. (What exactly is bad about overkilling?)
Do you know another, better way? If no, any cons I am missing about these three ways?
Javascript only, please.
I like the third way; I don't think it looks like overkill at all. If you need an even shorter way then this works too:
el.currentStyle.display.match(/(e-)?(block|cell)/)
But that's not very readable...
It might be worth abstracting it all away by extending the String prototype:
String.prototype.matches = function(what) {
return (',' + what + ',').indexOf(',' + this + ',') > -1;
};
// Using it:
el.currentStyle.display.matches('block,inline-block,table-cell');
If we're primarily aiming for readability, and if this is happening more than once -- perhaps even if it is just once -- I'd move the test to a function. Then define that function whichever way you like -- probably option 1, for max simplicity there.
Overkill? Possibly. But a gift to the programmer who wants to scan and understand the code 6 months from now. Probably you :-)
function isBlock(el) {
return (el.currentStyle.display == "block" ||
el.currentStyle.display == "inline-block" ||
el.currentStyle.display == "table-cell");
}
// ...
if (isBlock(el)) {
// do something
}
Can't you use the 2nd way but check if it's undefined and then skip the ": 1" part. I haven't tested though.
It looks like you need an inArray function, here is one from the top search result:
Array.prototype.inArray = function (value) {
var i;
for (i=0; i < this.length; i++) {
if (this[i] === value) {
return true;
}
}
return false;
};
Then the forth way would look like this:
if (['block','inline-block','table-cell'].inArray(el.currentStyle.display))
Or in a more readable manner:
var isBlock = ['block','inline-block','table-cell'].inArray(el.currentStyle.display);
My prefered solution for this is:
'block||inline-block||table-cell'.indexOf( el.currentStyle.display ) >= 0
I think that this will use native code of the string and be way more efficient than the array & iteration method.

Categories

Resources