How can I make the following code more elegant and readable?
if (this.flag === 1) {
this.value -= 0.1;
}
if (this.value <= 0) {
this.flag = 0;
}
if (this.flag === 0) {
this.value += 0.1;
}
if (this.value >= 1) {
this.flag = 1;
}
Edit: Let's say, for simplicity's sake, I'm changing the opacity of an object, and I want it to fade in and out from 0 to 1 over and over inside some sort of loop. .value is the opacity, and .flag is to tell it when to switch directions.
You could simplify some if-else scenarios with shorthand notations like below.
this.flag = this.value <= 0 ? 0 : 1;
this.value = this.value + (this.flag === 1 ? -0.1 : 0.1);
However, your script, in its current form, uses exclusive if conditions that don't cover all possible values of flag and value with an else block. Depending on whether you care about that, my proposal above could break your code.
EDIT - based on OP updates
flag should be a boolean true/false.
this.flag = this.value > 0;
this.value += (this.flag ? -0.1 : 0.1);
EDIT 2 - based on comments
Why should this.flag be manipulated through the value of this.value? The flag should be controlled through other means such as a checkbox or something, so your opacity change script should really be just this:
this.value += (this.flag ? -0.1 : 0.1);
If you're trying to auto-toggle the flag when the opacity reaches 0 or 1, you can do this:
this.value += (this.flag ? -0.1 : 0.1);
if(this.value === 1 || this.value === 0) {
this.flag = !this.flag;
}
Please note that this answer has already drifted outside of the scope of the question, which was to have a more elegant approach to conditionals. You'd be better off asking a new SO question if you need to discuss any further.
Based on what you have right now I would do this :
if (this.flag === 1) {
this.value -= 0.1;
} else if (this.flag === 0) {
this.value += 0.1;
}
if (this.value <= 0) {
this.flag = 0;
} else if (this.value >= 1) {
this.flag = 1;
}
But can flag be a boolean value? If so you don't need to the the numerical value check. Also can this.value be anywhere between 0-1? Flag isn't set in this case. If possible I would refactor the code like this but it depends on the logic you are trying to implement
if (this.flag) {
this.value -= 0.1;
} else {
this.value += 0.1;
}
if (this.value <= 0) {
this.flag = 0;
} else {
this.flag = 1;
}
This changes the outcome a bit, but I think it is what you actually want:
if (this.flag === 1) {
this.value -= 0.1;
if (this.value <= 0)
this.flag = 0;
} else /* if (this.flag === 0) */ {
this.value += 0.1;
if (this.value >= 1)
this.flag = 1;
}
While it still might not be elegant, it's at least easy to understand, as you only have a cyclomatic complexity of 4 (instead of 16 as in your original code).
For an elegant solution, you'd change even more. Instead of using a "flag" for the direction, you could represent the direction itself by the amount of change:
this.value += this.dir;
if (this.value >= 1)
this.dir = -0.1;
else if (this.value <= 0)
this.dir = 0.1;
Or then again, maybe even
this.value += this.dir;
if (this.value <= 0 || this.value >= 1)
this.dir *= -1;
Related
I am trying to see if I can reduce the code below with less JavaScript. When the user select Even dropdown option the only differences between that and the else statement is "i = 0" and "i % 2 === 0"
any recommendations? Maybe conditional operator would be best method? thank you
if (this.selection == "even") {
for (let i = 0; i <= number; i++) {
if (i % 2 === 0)
this.results.push(i + "\n");
}
} else {
for (let i = 1; i <= number; i++) {
if (i % 2 !== 0)
this.results.push(i + "\n");
}
}
Use this.selection to determine the starting number, then increment by 2 inside the loop instead of i++, so you don't have to check i % 2.
const startAt = this.selection == "even" ? 0 : 1;
for (let i = startAt; i <= number; i += 2) {
this.results.push(i + "\n");
}
Consider the following code (keyCode is used for backward compatibility):
/**
* Navigate through the items.
*
* #param {Event} event
* #return void
*/
navigate(event) {
if (event.keyCode === 38 || event.key === "ArrowUp") {
this.active = this.active + 1 > this.items.length ? 0 : this.active + 1;
}
if (event.keyCode === 40 || event.key === "ArrowDown") {
this.active = this.active - 1 < 0 ? this.items.length : this.active - 1;
}
}
If the above is not clear, what I am trying to do is the following:
When incrementing this.active, make sure it is not greater than the length of this.items, and if it is, return it to 0
When decrementing this.active, make sure it is not less than 0, and if it is, return it to the length of this.items
The above code works absolutely fine but I know that it can be done better and more efficiently. For instance, calling this.active -1 twice in inefficent.
Is there a way to gracefully achieve this using something along the lines of Math.min and Math.Max?
I'd use the modulo operator instead:
navigate(event) {
const { length } = items;
if (event.keyCode === 38 || event.key === "ArrowUp") {
this.active = (this.active + 1) % length;
} else if (event.keyCode === 40 || event.key === "ArrowDown") {
this.active = (this.active - 1 + length) % length;
}
}
I am trying to keep a variable within a range of numbers when the function is ran, but the if statement does not run.
I am trying to have the object move only if its between 0 and 50 , if the objects value X is trying to go to 55 it will not run the code, but the issue I am facing is if move.X value is 51 and move.Y is 7 nothing runs, I need it to where move.Y will still ran with the if statements, while move.X can only be ran backwards. (decreasing the numbers)
go(move) {
if (move.X > 0 && move.X < 50) {
if (move.Dir === "W") {
move.X -= 1;
} else if (move.Dir === "E") {
move.X += 1;
}
if (move.Y > 0 && move.Y < 50) {
if (move.Dir === "N") {
move.Y -= 1;
} else if (move.Dir === "S") {
move.Y += 1;
}
} else {
console.log("Too Far");
}
}
}
You could take a different approach and check the position along with the wanted direction.
function go(move) {
if (move.X > 0 && move.Dir === "W") move.X -= 1;
else if (move.X < 50 && move.Dir === "E") move.X += 1;
else if (move.Y > 0 && move.Dir === "N") move.Y -= 1;
else if (move.Y < 50 && move.Dir === "S") move.Y += 1;
else console.log("Too Far");
}
Your code has two problems:
1) If one of the values exits the range, e.g. X = -1, then there is no way to get back into the range again, as you block both directions in that case.
You should change your logic to only block the E if it reaches the left border and only the W if it reaches the right border, that way the object can be moved into the range again.
2) The last else part will only be executed if move.X > 0 && move.X < 50 is false. All the other nested ifs won't enter that branch.
You could either not nest the if / elseifs as shown in Nina's answer, or you would have to add an else branch to every if.
If move.Dir values are strictly associated with each coordinate (W and E - with X, while N and S - with Y), I would just try smth like:
function go(move) {
switch(move.Dir) {
case 'W':
if (move.X > 0) { move.X -= 1; }
break;
case 'E':
if (move.X < 50) { move.X += 1; }
break;
case 'N':
if (move.Y > 0) { move.Y -= 1; }
break;
case 'S':
if (move.Y < 50) { move.Y += 1; }
break;
}
}
I'm still pretty new to JavaScript and need to be pointed in the right direction on a tiny project that is just for practice.
Very sorry if I'm not posting incorrectly, this is my first post on Stack Overflow and any help is appreciated.
I've tried accomplishing my goal a few different ways and haven't gotten there.
attack.addEventListener("click", function(){
hit();
});
function hit(){
if (bossHealth.textContent > 0 && playerFocus.textContent >=2) {
playerFocus.textContent -= 2;
bossHealth.textContent -= 3;
console.log("attack");
}
else if (bossHealth.textContent >= 0 && playerFocus.textContent < 2){
alert("Attack costs 2 focus, try regenerating this turn to gain focus and health!");
}
};
strong.addEventListener("click", function(){
bigHit();
});
function bigHit(){
if(bossHealth.textContent > 0 && playerFocus.textContent >= 5){
playerFocus.textContent -= 6;
bossHealth.textContent -= 6;
console.log("strong attack");
}
else if (playerFocus <5){
alert("Strong attack costs 5 focus, if you do not have enough focus try regenerating for a turn");
}
else (bossHealth.textContent <= 0){
bossHealth.textContent = "Dead";
alert("You've killed the bad guy and saved the world!!!");
}
};
easy.addEventListener("click", function(){
reset();
});
function reset(){
playerHealth.textContent = 10;
playerFocus.textContent = 10;
bossHealth.textContent = 10;
};
hard.addEventListener("click", function(){
hardMode();
});
function hardMode(){
playerHealth.textContent = 10;
playerFocus.textContent = 10;
bossHealth.textContent = 15;
};
With function hit I don't get the alert in my else if statement
with function bigHit I also don't get my alert for the else if statement and neither part of the else statement works.
also subtraction works in my functions, but when trying to add another function that uses addition in the same way it adds the number to the end of the string instead of performing math
You really shouldn't depend on what is in the DOM for logic. You should try with local variables then update the DOM based on those variables.
var boss = {}
var player = {}
function reset(){
player.health = 10;
player.focus = 10;
boss.health = 10;
update()
};
function hit(){
if (boss.health > 0 && player.focus >=2) {
player.focus -= 2;
boss.health -= 3;
console.log("attack");
} else if (boss.health > 0 && player.focus < 2){
alert("Attack costs 2 focus, try regenerating this turn to gain focus and health!");
}
update()
}
function update() {
playerFocus.textContent = player.focus
playerHealth.textContent = player.health
bossHealth.textContent = boss.health
}
Your issue is caused by textContent automatically turning your numbers into strings, by managing the data using your own variables this doesn't affect your code's logic.
Alternatively you could parse the strings as numbers using new Number(playerFocus.textContent), parseFloat(playerFocus.textContent), or +playerFocus.textContent but your code will become very hard to read very quickly.
Hope this enough to help you to make more edits to your code.
It looks like your playerFocus variable is a DOM node? Based on that assumption, your condition is missing a check for its textContent:
if (bossHealth.textContent > 0 && playerFocus.textContent >= 5){
// ...
}
else if (playerFocus.textContent <5){
// ...
In JavaScript, else blocks cannot have conditions, so if you want to conditionally check whether bossHealth.textContent <= 0 at the end of your bigHit function, you will need to change it to an else if (bossHealth.textContent <= 0) { ... } block instead.
I'm adding on to #LoganMurphy's comment and my own. In order to use the value of bossHealth it needs to be an integer. Change your code to look something like this:
function hit(){
if (parseInt(bossHealth.textContent) > 0 && parseInt(playerFocus.textContent) >=2) {
playerFocus.textContent -= 2;
bossHealth.textContent -= 3;
console.log("attack");
}
else if (parseInt(bossHealth.textContent) >= 0 && parseInt(playerFocus.textContent) < 2){
alert("Attack costs 2 focus, try regenerating this turn to gain focus and health!");
}
};
https://www.w3schools.com/jsref/jsref_parseint.asp
I'm trying to figure out how to count the number of "holes" in a number. That is, where 8 has two holes and 0, 4, 6, 9 have one hole and the rest have none.
For some reason I'm getting a return of undefined and I'm pulling my hair out over it. Am I missing something?
var numOfHoles = 0;
for (i = 0; i < num.length; i++) {
if (num === 8) {
numOfHoles += 2;
}
else if (num === 0 || num === 4 || num === 6 || num === 9) {
numOfHoles++;
}
else
numOfHoles;
}
console.log(numOfHoles);
}
Simply take the number, split it into an array of ints, and then use an arrow function as the argument for reduce to total the correlating value from the number to holes map.
function numHoles(n){
return (""+n).split('').reduce((t,i) => t+=+"1000101021"[i],0);
}
document.write(numHoles(48488621597));
I guess you are looking for something like this :
var num = [9,4,5];
var numOfHoles = 0;
for (i = 0; i < num.length; i++){
if (num[i] == 8)
{
numOfHoles += 2;
}
else if (num[i]== 0 || num[i]== 4 || num[i]== 6 || num[i]== 9)
{
numOfHoles++;
}
}
console.log(numOfHoles);
You had multiple little error.
First of all you didn't need else { numOfHoles } which dont mean anything.
And you need if you try to check every elements to use the indexation of the element so you need to use num[i].
Unless there was some code missing from the top when you copied this over, it looks like you need to either remove the trailing bracket or declare this as a function (see below).
Edit: This is a strange question. Firstly, the answers referencing using an index on num might not work as expected. The easiest, but possibly not best, answer would be to convert the number to a string, then index and compare to characters instead of numbers.
As everyone else has mentioned, it makes things much easier if you maintain proper code format :)
function countNumHoles(num) {
var numOfHoles = 0;
var numStr = num.toString();
for (i = 0; i < num.toString().length; i++) {
if (numStr[i] === '8') {
numOfHoles += 2;
} else if (numStr[i] === '0' || numStr[i] === '4' || numStr[i] === '6' || numStr[i] === '9') {
numOfHoles++;
}
}
console.log(numOfHoles);
}
The problem is that you idented wrongly the code, so it's harder to see the errors. The correct identation of your code goes like this:
var numOfHoles = 0;
for (i = 0; i < num.length; i++) {
if (num === 8) {
numOfHoles += 2;
} else if (num === 0 || num === 4 || num === 6 || num === 9) {
numOfHoles++;
} else
numOfHoles;
console.log(numOfHoles);
}
So now with the correct identation you can easily see that else numOfHoles; isn't needed, num ins't defined and length is for string or arrays. Also console.log is better outside the loop in order to run only once. Here is a functional version:
var numOfHoles = 0;
limit = 5;
for (num = 0; num <= limit; num++) {
if (num === 8) {
numOfHoles += 2;
} else if (num === 0 || num === 4 || num === 6 || num === 9)
numOfHoles++;
}
console.log(numOfHoles);
Considering all the answers on top, you have some mistakes with braces. But you are counting num of holes in array, but you don't have indexes. Here is my test snippet. And it has nothing to do with empty numOfHoles. But i still recommend to remove it.
var numOfHoles = 0;
var num = [4,0,9,8,5,5,5];
for (i = 0; i < num.length; i++) {
if (num[i] === 8) {
numOfHoles += 2;
}
else if (num[i] === 0 || num[i] === 4 || num[i] === 6 || num[i] === 9) {
numOfHoles++;
}
else
numOfHoles;
}
console.log(numOfHoles);
It's kind of hard to answer. You used the length property in the variable "num". But strictly used the comparison. The rule if always be false, considering that (string "8" !== 8). This type of comparison requires that the variables have the same type.In this case or you use an integer or an Array and uses the variable i to access your elements in the loop for
Solution, or you comes in as a string and uses the following system [if (num === "8")], or working on becoming a int, or work with an array (recommended). To know the best solution we needed to see more of the code.
Your code should stay that way. Of course, if you need something more specific, let us know
var num = [0,2,8,5];
var numOfHoles = 0;
for (i = 0; i < num.length; i++){
if (num[i] == 8)
{
numOfHoles += 2;
}
else if (num[i]== 0 || num[i]== 4 || num[i]== 6 || num[i]== 9)
{
numOfHoles++;
}
}
console.log(numOfHoles);