Validating users input between min and max using a function - javascript

I'm trying to make a function that asks the user for a number in between whatever number is passed to the functions min and max, eg.(1,10) I can't seem to get it to work though, what am I missing/ doing wrong here?
function getProductChoice(min, max) {
do {
var productIndex = parseInt(prompt('Enter your product choice', '0'));
} while( isNaN(productIndex) || productIndex <= max || productIndex >= min);
getProductChoice(1,6);
};

I'm assuming you want to stop prompting when the given number satisifies the range. However, your current code does the opposite, continuing to run when the productIndex is less than the max or greater than the min. Try switching your max and min in the conditional.
In this example I've also pulled the getProductChoice() function call out of the function, as recursion is not necessary.
function getProductChoice(min, max) {
do {
var productIndex = parseInt(prompt('Enter your product choice', '0'));
} while( isNaN(productIndex) || productIndex <= min || productIndex >= max);
};
getProductChoice(1,6);

Related

NaN in the context of a recursive for loop

I am trying to understand a solution I found for a problem: "You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin."
My question is, if I run the function with change(3,[2]), why does it spit out 0. I am having trouble with comprehending how after a single recursive call currentCoin becomes undefined, and then when the program reaches the for loop in that call, it doesn't call the change function again with total += change(amount - 0 * undefined, coins.slice(0, -1)). Why does it not crash with either an infinite recursive call with change(NaN,[]) or coins.slice(0,-1) being used on an empty array. It seems to ignore that on the for loop.
Am I misunderstanding how a for-loop works?
var change = function(amount, coins) {
if(amount == 0) return 1;
let currentCoin = coins[coins.length - 1];
let total = 0;
for(let qty = 0; qty * currentCoin <= amount; qty++){
total += change(amount - qty * currentCoin, coins.slice(0, -1))
}
return total;
};
console.log(change(3,[2]))
There are a couple things going on here.
First is the behavior of coins[coins.length - 1]. In Javascript, when you access an element of a list at an index that doesn't exist in that list, the indexer will return undefined instead of crashing with an IndexOutOfBoundsException or the like.
Second is qty * currentCoin <= amount. In the case that currentCoin is undefined (due to the above), qty * currentCoin will be NaN. In Javascript, any comparison of NaN with another number will return false by design. (e.g. NaN <= anything is false).
Put this all together and you see that, on the first recursion, the coins array will be empty which makes currentCoin NaN. This causes qty * currentCoin <= currentAmount to be false, which causes the loop to short circuit (so slice never gets called on an empty list). Since the loop never executes, total will still be 0, which is what gets returned. This continues until qty * currentCoin <= amount becomes true in the outermost recursion, and that loop exits with total still equalling 0 (since it only ever added 0).
If you intersperse console.log calls in strategic places about the function, it becomes clearer what is happening:
var change = function(amount, coins) {
console.log(amount, coins);
if(amount == 0) return 1;
let currentCoin = coins[coins.length - 1];
console.log(':', currentCoin, amount);
let total = 0;
for(let qty = 0; qty * currentCoin <= amount; qty++){
total += change(amount - qty * currentCoin, coins.slice(0, -1))
console.log('=', total);
}
console.log('recdone');
return total;
};
console.log(change(3,[2]))
not crash because a NaN in comparison with a number is every false...
NaN < number or NaN > number and so on produce false... so the
qty * currentCoin <= amount
is evaluate false and will exit from the for.
So, if you need to check the NaN you must before the for
let totalCoin = qty * currentCoin;
let check = isNaN(totalCoin);
if(check) {
// return you sentinel value;
}
var change = function(amount, coins) {
if(amount == 0) return 1;
let currentCoin = coins[coins.length - 1]; // firstpass 1-1 = 0, second pas 0-1=-1 => coins[-1] = undefined
let total = 0;
// this will 0*0<=3, second pass 0*undefined => null which is false hence never execute
for(let qty = 0; qty * currentCoin <= amount; qty++){
total += change(amount - qty * currentCoin, coins.slice(0, -1))
}
return total;
};
console.log(change(3,[2]))
In second pass when coins.length = 0 then
let currentCoin = coins[0 - 1]; // = undefined
Later in for loop you will 0 * undefined ( qty * currentCoin) which results NaN which is Not a number
There is no need for recursion in this case. One can use a bottom-up dynamic programming approach. Let ways[i] denote the number of ways to get to i dollars with the given coins and coins[i] represent the value of the ith coin. Then, ways[i] is the sum of all ways[i - coins[j]] for all j from 1 to the number of coins.
var change = function(amount, coins) {
const ways = Array(amount + 1);
ways[0] = 1;
for(const coin of coins){
for(let i = coin; i <= amount; i++){
ways[i] = (ways[i] ?? 0) + ways[i - coin] ?? 0;
}
}
return ways[amount];
};
console.log(change(5,[1,2,3,4,5]))

Randomize multiple integers to equal variable

Using JavaScript, how would I go about generating 30 random integers and have the sum of those 30 integers be 60000? I need the script to produce a different set of numbers each time it is run, making sure that the total is always 60000
var n = 30;
var total = 60000;
var min = 10;
var max = 5000;
for (i = 0; i < n; i++) {
// Math.floor(Math.random()*(max-min+1)+min); ??
}
In order to avoid excessively large and small numbers, the min and max values will likely be needed
You can try something like this:
Logic
Accept Max total and total number of resulting Numbers.
Now loop based on this number - 1 for n-1 random numbers and last value should be max - currentSum. Since rest of numbers are random, this difference will also be random and this will also ensure total being equal.
Now all you need to do is return a random number based on a given range.
I have also added a flag to check for unique values. Currently I have just added 1 to it but this will not ensure its uniqueness, but as its out of scope, not rectifying it.
Code
function getRandomInRange(max) {
var raiseVal = Math.pow(10, (max.toString().length - 1) || 1);
return Math.floor(Math.random() * raiseVal) % max;
}
function getRandomNumbers(max, n, uniqueNum) {
var nums = [];
var sum = 0;
for (var i = 0; i < n - 1; i++) {
let r = getRandomInRange(max - sum);
if(uniqueNum && nums.indexOf(r) > -1){
r += 1;
}
nums.push(r)
sum += r;
}
nums.push(max - sum);
console.log(nums)
return nums
}
getRandomNumbers(3, 3, true)
getRandomNumbers(3, 3)
getRandomNumbers(1000, 10)
getRandomNumbers(600000, 30)

Limiting the Value of an input in JavaScript function

I'm creating the following web app for my employer:
https://jsfiddle.net/kupe2rLL/3/
How it Works: The Missed Opportunity and Engage Results fields populate dynamically as users enter information in the Company Info section.
The Problem: I need to program the app so that the Years in Business input takes no value less than 1 and no value greater than three when calculating the jobsRecovered variable and RevenueYear variable only.
<input type="text" id="yearsOpen" name="yearsOpen" class="form-control" onchange="calculateAll()" required>
var jobsRecovered = (averageJobs * 50) * recovery * yearsOpen;
var revenueYear = jobsRecovered * jobValue;
Preferably, I need the Years in Business input field to accept any value from 0 to infinity on the form input but then change the value of that variable to a minimum of 1 and maximum of 3 when making calculations for the jobsRecovered variable and RevenueYear only. I'd like to implement this solution using a JavaScript function if possible but I am open to alternate solutions.
I researched and implemented the following solution but unfortunately this only limits the min - max range of input on the form itself:
input.addEventListener('change', function(e) {
var num = parseInt(this.value, 10),
min = 1,
max = 3;
if (isNaN(num)) {
this.value = "";
return;
}
this.value = Math.max(num, min);
this.value = Math.min(num, max);
});
Any assistance would be appreciated. (Note: My experience with programming is Beginner level.)
To limit the range of the variable value, you can pass it through if-else statements, or a ternary operation which act like a filter for your values.
e.g.
if (yearsOpen <= 3) ? (if (yearsOpen < 1) ? (yearsOpen = 1) : (yearsOpen = yearsOpen)) : (yearsOpen = 3);
smaller statement if (n <= 3) ? (if (n < 1) ? (n = 1) : (n = n)) : (n = 3);
If-else format:
if (yearsOpen <= 3) {
if (yearsOpen < 1) {
yearsOpen = 1;
} else {
yearsOpen = yearsOpen;
}
} else {
yearsOpen = 3;
}
Hope that makes sense.

Random integer in a certain range excluding one number

I would like get a random number in a range excluding one number (e.g. from 1 to 1000 exclude 577). I searched for a solution, but never solved my issue.
I want something like:
Math.floor((Math.random() * 1000) + 1).exclude(577);
I would like to avoid for loops creating an array as much as possible, because the length is always different (sometimes 1 to 10000, sometimes 685 to 888555444, etc), and the process of generating it could take too much time.
I already tried:
Javascript - Generating Random numbers in a Range, excluding certain numbers
How can I generate a random number within a range but exclude some?
How could I achieve this?
The fastest way to obtain a random integer number in a certain range [a, b], excluding one value c, is to generate it between a and b-1, and then increment it by one if it's higher than or equal to c.
Here's a working function:
function randomExcluded(min, max, excluded) {
var n = Math.floor(Math.random() * (max-min) + min);
if (n >= excluded) n++;
return n;
}
This solution only has a complexity of O(1).
One possibility is not to add 1, and if that number comes out, you assign the last possible value.
For example:
var result = Math.floor((Math.random() * 100000));
if(result==577) result = 100000;
In this way, you will not need to re-launch the random method, but is repeated. And meets the objective of being a random.
As #ebyrob suggested, you can create a function that makes a mapping from a smaller set to the larger set with excluded values by adding 1 for each value that it is larger than or equal to:
// min - integer
// max - integer
// exclusions - array of integers
// - must contain unique integers between min & max
function RandomNumber(min, max, exclusions) {
// As #Fabian pointed out, sorting is necessary
// We use concat to avoid mutating the original array
// See: http://stackoverflow.com/questions/9592740/how-can-you-sort-an-array-without-mutating-the-original-array
var exclusionsSorted = exclusions.concat().sort(function(a, b) {
return a - b
});
var logicalMax = max - exclusionsSorted.length;
var randomNumber = Math.floor(Math.random() * (logicalMax - min + 1)) + min;
for(var i = 0; i < exclusionsSorted.length; i++) {
if (randomNumber >= exclusionsSorted[i]) {
randomNumber++;
}
}
return randomNumber;
}
Example Fiddle
Also, I think #JesusCuesta's answer provides a simpler mapping and is better.
Update: My original answer had many issues with it.
To expand on #Jesus Cuesta's answer:
function RandomNumber(min, max, exclusions) {
var hash = new Object();
for(var i = 0; i < exclusions.length; ++i ) { // TODO: run only once as setup
hash[exclusions[i]] = i + max - exclusions.length;
}
var randomNumber = Math.floor((Math.random() * (max - min - exclusions.length)) + min);
if (hash.hasOwnProperty(randomNumber)) {
randomNumber = hash[randomNumber];
}
return randomNumber;
}
Note: This only works if max - exclusions.length > maximum exclusion. So close.
You could just continue generating the number until you find it suits your needs:
function randomExcluded(start, end, excluded) {
var n = excluded
while (n == excluded)
n = Math.floor((Math.random() * (end-start+1) + start));
return n;
}
myRandom = randomExcluded(1, 10000, 577);
By the way this is not the best solution at all, look at my other answer for a better one!
Generate a random number and if it matches the excluded number then add another random number(-20 to 20)
var max = 99999, min = 1, exclude = 577;
var num = Math.floor(Math.random() * (max - min)) + min ;
while(num == exclude || num > max || num < min ) {
var rand = Math.random() > .5 ? -20 : 20 ;
num += Math.floor((Math.random() * (rand));
}
import random
def rng_generator():
a = random.randint(0, 100)
if a == 577:
rng_generator()
else:
print(a)
#main()
rng_generator()
Exclude the number from calculations:
function toggleRand() {
// demonstration code only.
// this algorithm does NOT produce random numbers.
// return `0` - `576` , `578` - `n`
return [Math.floor((Math.random() * 576) + 1)
,Math.floor(Math.random() * (100000 - 578) + 1)
]
// select "random" index
[Math.random() > .5 ? 0 : 1];
}
console.log(toggleRand());
Alternatively, use String.prototype.replace() with RegExp /^(577)$/ to match number that should be excluded from result; replace with another random number in range [0-99] utilizing new Date().getTime(), isNaN() and String.prototype.slice()
console.log(
+String(Math.floor(Math.random()*(578 - 575) + 575))
.replace(/^(577)$/,String(isNaN("$1")&&new Date().getTime()).slice(-2))
);
Could also use String.prototype.match() to filter results:
console.log(
+String(Math.floor(Math.random()*10))
.replace(/^(5)$/,String(isNaN("$1")&&new Date().getTime()).match(/[^5]/g).slice(-1)[0])
);

Regex for comma separated numbers within specific range

I need regex that validated the comma separated numbers with min and max number limit. i tried this regex but no luck
^(45|[1-9][0-5]?)$
i want the list like this one (min number is 1 and max number is 45)
23,45,3,7,1,9,34
It isn't a job for regex (regex are not handy with numbers, imagine the same with a more complicated range like (247,69352) that needs to build a giant pattern). So my advice is to split your string and then to check your items one by one.
A way without regex:
function numsInRange(s, min, max) {
var items = s.split(',');
for (var i in items) {
var num = parseInt(items[i], 10);
if (num != items[i] || num < min || num > max)
return false;
}
return true;
}
var s='23,45,3,7,1,9,34';
console.log(numsInRange(s, 1, 45)); // true
console.log(numsInRange(s, 1, 44)); // false
demo
This regex will do the job:
\b(([1-3][0-9])|(4[0-5])|[1-9])\b
I would be tempted to use a combination of regex (depending on need) and array iterators (not for..in enumeration) (ES5 in example)
var reIsPattern = /^[+\-]?(?:0|[1-9]\d*)$/;
function areNumsInRange(commaString, min, max) {
return commaString.split(',')
.map(Function.prototype.call, String.prototype.trim)
.every(function(item) {
return reIsPattern.test(item) && item >= min && item <= max
});
}
var s = '23,45,3,7,1,9,34';
document.body.textContent = areNumsInRange(s, 1, 45);
Or perhaps just simply
function areNumsInRange(commaString, min, max) {
return commaString.split(',').every(function(item) {
var num = Number(item);
return num >= min && num <= max;
});
}
var s = '23,45,3,7,1,9,34';
document.body.textContent = areNumsInRange(s, 1, 45);

Categories