How can I convert these 'if' statements to an algorithm - javascript

I'm trying to convert these 'if' statements to an algorithm, so that I can have many more stages instead of being limited to 10. How would I go about converting it? I just can't wrap my head around the logic!
function getStage(km) {
if (km > 2 && km < 4){
return 1;
}
else if (km > 4 && km < 6){
return 2;
}
else if (km > 6 && km < 8){
return 3;
}
else if (km > 8 && km < 10){
return 4;
}
else if (km > 10 && km < 12){
return 5;
}
else if (km > 12 && km < 14){
return 6;
}
else if (km > 14 && km < 16){
return 7;
}
else if (km > 16 && km < 18){
return 8;
}
else if (km > 18 && km < 20){
return 9;
}
else if (km > 20 && km < 22){
return 10;
}
}
I have tried this:
function getStage(km) {
var tempStage = 0;
for (var i = 0; i < stages; i++) {
var stage = i + 1;
var tempKm = stage * 2;
var nextKm = stage + 2;
if (km > tempKm && km < nextKm) {
tempStage = stage;
}
}
return tempStage;
}
Perhaps I shouldn't be using a for loop? Is there a better way of doing this?

Maybe you are looking for Math.floor
function getStage(km) {
return Math.floor(km / 2)
}
console.log(getStage(2));
// 1
console.log(getStage(10));
// 5
console.log(getStage(11));
// 5

You could take the floored value of km divided by two plus one.
10 is the upper limit.
function getStage(km) {
return Math.min((km >> 1) + 1, 10);
}
console.log(getStage(2.1)); // 2
console.log(getStage(3)); // 2
console.log(getStage(4)); // 4

You can just use math to do this. No loops or conditionals necessary.
Notice that your input intervals increase in "steps" of 2, and your outputs increase in "steps" of 1. This makes me think maybe we should divide km by 2.
Since we always want an integer answer, we can use the floor function.
Some examples:
floor(3/2) = 1
floor(4.1/2) = 2
etc.
Depending on what you want to return for edge cases (what if km = 2 or km = 4, or any multiple of 2?) we might be done here. If you wish to return 1 when k=4, 2 when k=6, etc., you'll need to do a little more work.
* In general: * if you are working with numbers and you find yourself writing a lot of cases, you can usually use some combination of simple mathematical operators to calculate the result. For problems like these, try thinking about your input/output pairs, and what the relationship is between them.

Try something like this. This is general solution which applies not only to 2*n series.
const stages = [2, 4, 6, 8, 10, 13, 15, 16, 20]; //should be ordered. Else sort it first.
const getStage = km => stages.indexOf(stages.find(i => km < i));
console.log(getStage(5)); //2
console.log(getStage(1.5)); //0
console.log(getStage(8.1)); //4
console.log(getStage(15)); //7
console.log(getStage(22)); //-1 out of index

Related

JavaScript: Get the second digit from a number?

I have a number assigned to a variable, like that:
var myVar = 1234;
Now I want to get the second digit (2 in this case) from that number without converting it to a string first. Is that possible?
So you want to get the second digit from the decimal writing of a number.
The simplest and most logical solution is to convert it to a string :
var digit = (''+myVar)[1];
or
var digit = myVar.toString()[1];
If you don't want to do it the easy way, or if you want a more efficient solution, you can do that :
var l = Math.pow(10, Math.floor(Math.log(myVar)/Math.log(10))-1);
var b = Math.floor(myVar/l);
var digit = b-Math.floor(b/10)*10;
Demonstration
For people interested in performances, I made a jsperf. For random numbers using the log as I do is by far the fastest solution.
1st digit of number from right = number % 10 = Math.floor((number / 1) % 10)
1234 % 10; // 4
Math.floor((1234 / 1) % 10); // 4
2nd digit of number from right = Math.floor((number / 10) % 10)
Math.floor((1234 / 10) % 10); // 3
3rd digit of number from right = Math.floor((number / 100) % 10)
Math.floor((1234 / 100) % 10); // 2
nth digit of number from right = Math.floor((number / 10^n-1) % 10)
function getDigit(number, n) {
return Math.floor((number / Math.pow(10, n - 1)) % 10);
}
number of digits in a number = Math.max(Math.floor(Math.log10(Math.abs(number))), 0) + 1 // Credit to: https://stackoverflow.com/a/28203456/6917157
function getDigitCount(number) {
return Math.max(Math.floor(Math.log10(Math.abs(number))), 0) + 1;
}
nth digit of number from left or right
function getDigit(number, n, fromLeft) {
const location = fromLeft ? getDigitCount(number) + 1 - n : n;
return Math.floor((number / Math.pow(10, location - 1)) % 10);
}
Get rid of the trailing digits by dividing the number with 10 till the number is less than 100, in a loop. Then perform a modulo with 10 to get the second digit.
if (x > 9) {
while (x > 99) {
x = (x / 10) | 0; // Use bitwise '|' operator to force integer result.
}
secondDigit = x % 10;
}
else {
// Handle the cases where x has only one digit.
}
You know, I get that the question asks for how to do it without a number, but the title "JavaScript: Get the second digit from a number?" means a lot of people will find this answer when looking for a way to get a specific digit, period.
I'm not bashing the original question asker, I'm sure he/she had their reasons, but from a search practicality standpoint I think it's worth adding an answer here that does convert the number to a string and back because, if nothing else, it's a much more terse and easy to understand way of going about it.
let digit = Number((n).toString().split('').slice(1,1))
// e.g.
let digit = Number((1234).toString().split('').slice(1,1)) // outputs 2
Getting the digit without the string conversion is great, but when you're trying to write clear and concise code that other people and future you can look at really quick and fully understand, I think a quick string conversion one liner is a better way of doing it.
function getDigit(number, indexFromRight) {
var maxNumber = 9
for (var i = 0; i < indexFromRight - 2; i++) {
maxNumber = maxNumber * 10 + 9
}
if (number > maxNumber) {
number = number / Math.pow(10, indexFromRight - 1) | 0
return number % 10
} else
return 0
}
A "number" is one thing.
The representation of that number (e.g. the base-10 string "1234") is another thing.
If you want a particular digit in a decimal string ... then your best bet is to get it from a string :)
Q: You're aware that there are pitfalls with integer arithmetic in Javascript, correct?
Q: Why is it so important to not use a string? Is this a homework assignment? An interview question?
I don’t know why you need this logic, but following logic will get you the second number
<script type="text/javascript">
var myVal = 58445456;
var var1 = new Number(myVal.toPrecision(1));
var var2 = new Number(myVal.toPrecision(2));
var rem;
rem = var1 - var2;
var multi = 0.1;
var oldvalue;
while (rem > 10) {
oldvalue = rem;
rem = rem * multi;
rem = rem.toFixed();
}
alert(10-rem);
</script>
function getNthDigit(val, n)
{
var modVal = val % Math.pow(10,n);//Remove all digits larger than nth
return Math.floor(modVal / Math.pow(10,n-1));//Remove all digits less than nth
}
Just a simple idea to get back any charter from a number as a string or int:
const myVar = 1234;
String(myVar).charAt(1)
//"2"
parseInt(String(myVar).charAt(1))
//2
var newVar = myVar;
while (newVar > 100) {
newVar /= 10;
}
if (newVar > 0 && newVar < 10) {
newVar = newVar;
}
else if (newVar >= 10 && newVar < 20) {
newVar -= 10;
}
else if (newVar >= 20 && newVar < 30) {
newVar -= 20;
}
else if (newVar >= 30 && newVar < 40) {
newVar -= 30;
}
else if (newVar >= 40 && newVar < 50) {
newVar -= 40;
}
else if (newVar >= 50 && newVar < 60) {
newVar -= 50;
}
else if (newVar >= 60 && newVar < 70) {
newVar -= 60;
}
else if (newVar >= 70 && newVar < 80) {
newVar -= 70;
}
else if (newVar >= 80 && newVar < 90) {
newVar -= 80;
}
else if (newVar >= 90 && newVar < 100) {
newVar -= 90;
}
else {
newVar = 0;
}
var secondDigit = Math.floor(newVar);
That's how I'd do it :)
And here's a JSFiddle showing it works :) http://jsfiddle.net/Cuytd/
This is also assuming that your original number is always greater than 9... If it's not always greater than 9 then I guess you wouldn't be asking this question ;)

javascript number counting

I am slightly stuck on the javascript logic to accomplish this.
Basically
If I give a number (say 30)
I want to show 5 either side.
so
25 26 27 28 29 30 31 32 33 34 35
That part is easy.
But then I need to handle cases where the number is below 5 (say 3).
What I want to to is,
for every number not shown on the right,
add it to the left
so
1 2 3 4 5 6 7 8 9 10 11
But then I need to handle cases where the number is above a (maximum-5) (say maximum = 100, number = 98).
What I want to to is,
for every number not shown on the left,
add it to the right
so
90 91 92 93 94 95 96 97 98 99 100
But then I need to handle cases where the maximum is below 10 (say number = 3, maximum = 8
What I want to to is,
only show the applicable range
so
1 2 3 4 5 6 7 8
But I am not sure on the logic
function ranger(num) {
//Establish limits and pre/post array storage
var low = 0, high = 100, howMany = 5;
var pre = [];
var post = [];
//Increment/decrement if appropriate
for(x=1;x<=howMany;x++) {
if((num-x) > low) { pre.push(num-x); }
if((num+x) < high) { post.push(num+x); }
}
pre.reverse();
alert("Before: "+pre+'\nNumber: '+num+'\nAfter: '+post)
}
ranger(7);
ranger(2);
ranger(96);
Tested for all your cases:
range = 5;
maximum = 8;
number = 3;
left = right = number;
while(right - left < range*2 ) {
if (right + 1 <= maximum) {
right++;
}
if (left - 1 > 0 ) {
left--;
}
if (right == maximum && left == 1) {
break;
}
}
for(i=left;i<=right;i++) {
console.log(i);
}
One possible solution:
function getPages(fromPageNumber) {
var result = [];
fromPageNumber= Math.min(94, Math.max(6, fromPageNumber));
for(var i = -5; i <=5; i++)
result.push(fromPageNumber + i);
return result;
}
// Set up your limits and bounds
var radius = 5.
middleNumber = 20,
lowerBound = 1,
upperBound = 100;
// For the defined (and available range) create an array of valid numbers
var results = [];
for (int i = Math.max(middleNumber - radius, lowerBound);
i <= Math.min(middleNumber + radius, upperBound);
i++) {
results.push(i);
}
// Print out the resulting numbers with spaces in between
console.log(results.join(' '));
function getSequence(num, length)
{
var min = 0;
var max=100;
Array result;
for(int i=num-(length/2); i<num+(length/2);i++)
{
if(i>min && i< max)
result.add(i);
}
return result;
}

Rounding up to the nearest multiple of 5

I am trying to write a Javascript function rounding up to the nearest multiple of 5. But you only round up if the difference between the variable and the roundup to the multiple of 5 is less than 3. (so 53 would round up to 55, but 52 would stay 52.)
Also if the grade is < 38 its an F.
The code below is what I have but it is not running properly. How can I fix it?
Thanks!!
grade = 71
function roundGrade (grade) {
const remainder = grade % 5
if (grade < 38) {
return "fail"; }
else if (remainder >= 3) {
grade; }
else if (remainder < 3) {
grade-remainder+5
}
}
If the remainder is 3 or above, you simply need to add 5 and subtract the remainder, which can be done with grade += 5 - remainder. Also note that you don't need your second else if conditional at all, as you only want to modify grade if the remainder is greater than or equal to 3. Finally, you need to make sure that your function actually returns grade with return grade.
This can be seen in the following:
function roundGrade(grade) {
const remainder = grade % 5;
if (grade < 38) {
return "fail";
} else if (remainder >= 3) {
grade += 5 - remainder;
}
return grade;
}
console.log(roundGrade(71));
console.log(roundGrade(74));
calculate the remainder of grade / 5 and add it to the grade if the remaining is less than 3, otherwise return the grade as is.
const grade1 = 71
const grade2 = 73
const grade3 = 33
function roundGrade(grade) {
if (grade < 38) return "fail"
const rem = grade % 5;
return rem < 3 ? grade : grade + (5 - rem)
}
console.log(roundGrade(grade1))
console.log(roundGrade(grade2))
console.log(roundGrade(grade3))

Any Suggestions for Improving my Code on (Negative) Factorial (in JavaScript)?

I was doing a basic problem on coding a factorial n!:
function factorialize(num) {
if (num < 0) {
return undefined;
} else if (num === 0) {
return 1;
}
else {
return num * factorialize(num - 1);
}
}
The site I'm learning from accepted this solution, but they're only testing for nonnegative integers, i.e for n ≥ 0, not negative integers.
I got curious on how to compute a negative factorial (-n)!. Many pages say it's undefined, but I found two saying it could be defined.
Link 1
Link 2
The gist I got is that:
|n!| = |(-n)!|
Their absolute values are the same but the negative factorials change signs.
Examples:
4! = (-4)! = 24
5! = 120 but (-5)! = -120
The formula that I gathered from the two linked pages is:
(-n)! = |n|! * (-1)^n
And this reflects my code. From test cases, I think I've nailed it. I just want to ask if there's a better way of coding it. Someone from here
remarked that using recursion is memory-inefficient.
function factorialize(num) {
if (num === 0) {
return 1;
} else if (num > 0) {
return num * factorialize(num - 1);
} else {
return Math.pow(-1, num) * Math.abs(num) * factorialize(Math.abs(num) - 1);
}
}
// test cases below
console.log(factorialize(-1)); // -1
console.log(factorialize(1)); // 1
console.log(factorialize(0)); // 1
console.log(factorialize(-2)); // 2
console.log(factorialize(2)); // 2
console.log(factorialize(-3)); // -6
console.log(factorialize(3)); // 6
console.log(factorialize(-4)); // 24
console.log(factorialize(4)); // 24
console.log(factorialize(-5)); // -120
console.log(factorialize(5)); // 120
You could also do this using iteration (normally everything that can be done recursively can also be done iteratively). There is also no need to raise -1 to the power of your number. It would be much more efficient, if your just checked if it was odd or even.
function factorialize(n){
var absNum = Math.abs(n);
var i = 1;
var factorial = 1;
while(i <= absNum){
factorial *= i;
i += 1;
}
if(absNum % 2 === 1 && n < 0){
return -factorial
}
return factorial;
}

JavaScript: Math.random: How to choose a random number between 190 and 255 “unevenly”, not “evenly”?

If you want to choose a random number between 190 and 255, expecting evenly distributed results, the code you need is as simple as the following, correct?
190 + Math.floor(Math.random() * 66);
But what if you prefer unevenly distributed results between 190 and 255? I mean, for instance, the closer a number is to the lower end of the range (i.e. 190), the higher the possibility of the number being chosen.
Let us suppose that the code returns a random number:
between 190 and 210 with a 70% probability.
between 211 and 230 with a 20% probability.
between 231 and 255 with a 10% probability.
I think that the uneven distribution like this adds an interesting flavour to the act of choosing a random number.
I've written the code for this in two different ways, the second one taking a more complex form. I started to learn programming just several days ago, so I've written them, drawing on what little I know of JavaScript at the moment. I wonder if I can express them more efficiently.
Incidentally, I have one specific question in mind:
In the 1st Code, do I need to place var in front of attackPoint = midRange and attackPoint = highRange in the if/else statement?
1st Code:
var lowRange = 190 + Math.floor(Math.random() * 21);
var midRange = 211 + Math.floor(Math.random() * 20);
var highRange = 231 + Math.floor(Math.random() * 25);
var randomHundred = 1 + Math.floor(Math.random() * 100);
if (randomHundred <= 70) {
var attackPoint = lowRange;
}
else if (randomHundred <= 90) {
var attackPoint = midRange;
}
else {
var attackPoint = highRange;
}
console.log(attackPoint);
2nd Code:
var lowRange = 190 + Math.floor(Math.random() * 21);
var midRange = 211 + Math.floor(Math.random() * 20);
var highRange = 231 + Math.floor(Math.random() * 25);
var emptyArray = [];
for (var i = 0; i < 100; i++) {
if (i < 70) {
emptyArray.push(lowRange);
}
else if (i < 90) {
emptyArray.push(midRange);
}
else {
emptyArray.push(highRange);
}
};
var attackPoint = emptyArray[Math.floor(Math.random() * 100)];
console.log(attackPoint);
The 1st way improvement:
You don't need to calculate random values for all ranges, you only need the selected range. Also it's unnecessary to cast 0..1 range to 0..100. You can deal with 0..1 range directly.
var q = Math.random(), attackPoint;
if (q < 0.7) {
attackPoint = 190 + Math.floor(Math.random() * 21);
}
else if (q < 0.9) {
attackPoint = 211 + Math.floor(Math.random() * 20);
}
else {
attackPoint = 231 + Math.floor(Math.random() * 25);
}
console.log(attackPoint);
On your specific point, I am not a Javascript expert, but I think you need:
var attackPoint; // Declare, but do no initialize the variable.
if (randomHundred <= 70) {
attackPoint = lowRange;
}
else if (randomHundred <= 90) {
attackPoint = midRange;
}
else {
attackPoint = highRange;
}
A more efficient way is going to involve "stretching" the range you want to be more likely, and then choosing randomly. Something like:
var attackValues = []
for (var i = 190; i < 211; i++) {
for (var j = 0; j < 7; j++) {
attackValue.push(i); // Seven values here.
}
}
for (var i = 211; i < 231; i++) {
attackValue.push(i); attackValue.push(i); // Two values here
}
for (var i = 231; i < 256; i++) {
attackValue.push(i); // Just one value here.
}
// The attackValue array could be calculated once and stored for repeated
// use if your inflection points stay constant.
var attackPoint = attackValue[ Math.floor(Math.random() * attackValue.length) ]
The idea is to fill attackValue array with the possible results, putting in multiple copies of the values you want more often, and then choosing one at random.
Another approach is to do something like square the random value, and then convert to integer. Squaring makes smaller values more likely in a somoother way than adding specific inflexion points. Depending on your application, that may be better (or not).
Just from a programming style it is good form to separate the table of probabilities from the logic that generates the random numbers. The reason being that the exact details of the probabilities and ranges are likely to change over time, but the idea of a weighted random number will stay constant. So once you've got the weighted random number code changing, you don't want to have to change it time and again:
var table = [
{ probability:70, low:190, hi: 210},
{ probability:20, low:211, hi: 230},
{ probability:10, low:231, hi: 255}
]
function weightedRandom(table) {
var total = table.reduce(function(sum, line) {
return sum + line.probability;
}, 0);
var select = Math.floor(Math.random() * total);
for (var i = 0; i < table.length; i++) {
var line = table[i];
select -= table[i].probability;
if (select < 0) {
return (Math.floor(Math.random() * (line.hi - line.low)) +
line.low;)
}
}
}
for (var i = 0; i < 10; i++) {
console.log(weightedRandom(table));
}
NOTE: you could simplify the above code if you assume that the probabilities will always add to 100.
When I run this I get:
237
228
239
209
193
218
213
193
245
214
which seems correct.
I think this might be a possible solution too:
var number = Math.random();
var index = Math.floor(number * 66) + 190;
//answer:
console.log(Math.floor(number * dist(index) * 66) + 190);
function dist(index) {
var retValue;
if (index < 210) {
retValue = .7;
}
else if (number >= 210 && number < 230) {
retValue = .2;
}
else {
retValue = .1;
}
return retValue;
}

Resources