If I supply a number to a function, how would I go about validating it against a range of numbers like this?
1-10 = A
11-20 = B
21-30 = C
...
I know I can do if statements to evaluate this, but I'm looking for something more elegant because the problem gets a lot more complex and I don't want a nasty web of ifs.
var letter = "";
function getLetter(num) {
if (num >= 1 && num <= 10) {
letter = "A";
} else if (num >= 11 && num <= 20) {
letter = "B";
}
// this eventually gets gross
}
Expected outcome of getLetter(14) would be "B", and getLetter(49) would be "E", etc. Case/switch is also off the table for similar reasons.
Any other ideas welcome.
Just a point about your code
function getLetter(num) {
if (num >= 1 && num <= 10) {
letter = "A";
} else if (num >= 11 && num <= 20) {
letter = "B";
}
// this eventually gets gross
}
this can be simplified to
function getLetter(num) {
if (num >= 1) {
if(num <= 10) {
letter = "A";
} else if (num <= 20) {
letter = "B";
}
// this eventually gets gross too
}
}
But:
If it's as simple as every letter represents a range of 10 values:
function getLetter(num) {
return String.fromCharCode(65 + Math.floor((num - 1) / 10));
}
console.log(getLetter(1));
console.log(getLetter(14));
console.log(getLetter(49));
or as suggested
function getLetter(num) {
const ret = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return ret[Math.floor((num - 1) / 10)] || "+"; // greater than 260
}
console.log(getLetter(1));
console.log(getLetter(14));
console.log(getLetter(49));
console.log(getLetter(261));
function getLetter(number) {
let ranges = {
a: 10,
b: 20,
c: 30,
underflow: 0,
overflow: Infinity
}
return Object.entries(ranges)
.sort(([ka, va], [kb, vb]) => va - vb) // because object key sort order isn't guaranteed
// though it will be in the order as declared, but
// sorting makes sense
.find(([key, value]) => number <= value)[0];
}
console.log(getLetter(5))
console.log(getLetter(17))
console.log(getLetter(20))
console.log(getLetter(30))
console.log(getLetter(31))
console.log(getLetter(0))
If the ranges are contiguous, you only need one of the boundaries
Works alright if you want to put your ranges into an object, and then loop through that
function getLetter (number) {
let ranges = {
a: [1, 10],
b: [11, 20],
c: [21, 30],
d: [31, 36],
e: [37, 40]
}
return Object.keys(ranges).find((key) => {
let currRange = ranges[key];
if (number >= currRange[0] && number <= currRange[1]) {
return key;
}
});
}
console.log(getLetter(5))
console.log(getLetter(17))
console.log(getLetter(20))
console.log(getLetter(30))
console.log(getLetter(35))
console.log(getLetter(39))
Related
Challenge: https://www.codewars.com/kata/57c7930dfa9fc5f0e30009eb/train/javascript
Hi I have been trying this problem for many hours but unfortunately my code is taking too long to pass:
function closestPower(num) {
num = Math.floor(num);
if (num < 4) return 4;
// check if input is perfect power
let base = 2;
while (base < 10) {
let exponent = Math.trunc(getBaseLog(base , num));
if ( Math.pow(base, exponent) === num ) {
return num;
}
base++;
}
// check for upper and lower
base = 2;
const verifyObj = {upper:null, lower:null}; // verify
let upperPower = num + 1;
let lowerPower = num - 1;
while (!verifyObj.upper || !verifyObj.lower)
{
// no perfect power
if (lowerPower <= 2 ) verifyObj.lower = "Not found";
if (upperPower === Infinity ) verifyObj.upper = "Not found";
// up til base 9
if (base === 10) {
if (!verifyObj.upper) upperPower++;
if (!verifyObj.lower) lowerPower--;
base = 2;
}
// upper
if (!verifyObj.upper) {
let exponent = Math.trunc(getBaseLog(base , upperPower));
if ( Math.pow(base, exponent) === upperPower ) {
verifyObj.upper = upperPower;
}
}
// lower
if (!verifyObj.lower) {
let exponent = Math.trunc(getBaseLog(base , lowerPower));
if ( Math.pow(base, exponent) === lowerPower ) {
verifyObj.lower = lowerPower;
}
}
base++;
}
console.log(verifyObj) // {upper:64, lower: 49}
// nearest power
if ((upperPower - num) < (num - lowerPower)) {
return upperPower;
}
else return lowerPower;
}
closestPower(56.5); // 49
function getBaseLog(x, y) {
return Math.log(y) / Math.log(x);
}
I realized that my code is redundant as all i need to know if a “base” and “exponent” are more than 1 to determine a perfect power. Any formulas or ideas?
Some issues:
There is no reason why base should not be allowed to be 10 or more
Trying with upperPower at each increment is taking too many iterations. The distance to the next power might be rather big.
I would suggest the following algorithm:
Let the exponent to try with start at 2, and then increment by 1. Calculate which could be the corresponding base. The real base can be found by raising n to the inverse exponent (i.e. 1/exp). Then there are only 2 interesting integer bases to consider: by rounding downwards and upwards.
Here is an implementation:
function closestPower(n) {
if (n <= 6) return 4;
let result = -1;
let closest = n;
for (let factor, exp = 2; (factor = n ** (1 / exp)) > 1.9; ++exp) {
let above = Math.ceil(factor);
for (let intfactor = Math.floor(factor); intfactor <= above; intfactor++) {
let power = intfactor ** exp;
let diff = Math.abs(power - n);
if (diff == 0) return n;
if (diff < closest || diff == closest && power < n) {
closest = diff;
result = power;
}
}
}
return result;
}
// Some tests:
const tests = [
[0, 4], [9, 9], [30, 32], [34, 32], [56.5, 49],
[123321456654, 123321773584]
];
for (let [n, expected] of tests) {
let result = closestPower(n);
if (result === expected) continue;
console.log(`closestPower(${n}) returned ${result}, but expected ${expected}`);
}
console.log("all tests done");
Here's my algorithm
first i will get the exponent from base that less than of the n then I added the current base of the loop with the n then get the base log.
function closestPower(n) {
if(n < 4) return 4
let closest = []
let base = 2
while(base < n) {
const exponent = Math.floor(Math.log(n + base) / Math.log(base))
const power = Math.pow(base,exponent)
if(exponent === 1) break
if(power === n) return n
closest.push(power)
base++
}
return closest.reduce((prev, curr) => (Math.abs(curr - n) < Math.abs(prev - n) ? curr : prev))
}
console.log(closestPower(0))
console.log(closestPower(9))
console.log(closestPower(30))
console.log(closestPower(34))
console.log(closestPower(56.5))
console.log(closestPower(123321456654))
I am trying to take the average of 3 grades for three student (stored in an array of arrays), and then run those averages through a function with an else if statement to check whether the average grades are each and A,B or C.
I would prefer not to have to make a separate function with an else if for each students average (so I would know how to scale this to more than 3 inputs), and I am not sure how I can index the averageGrades array in the function so that I can console.log each element (student) of the averageGrades array and have the else if statement evaluate that particular element (student).
I also tried making an averageGrade variable for each student so that the averageGrades array had single values and not a full equation but ran into the same problem.
var studentGrades = [
[80, 90, 94],
[80, 90, 94],
[80, 90, 94]
]
var studentAvgerages = [
(studentGrades[0][0] + studentGrades[0][1] + studentGrades[0][2]) / 3,
(studentGrades[1][0] + studentGrades[1][1] + studentGrades[1][2]) / 3,
(studentGrades[2][0] + studentGrades[2][1] + studentGrades[2][2]) / 3
]
for (var i = 0; i <= studentAvgerages.length; i++) {
function evalGrades(studentAvgerages[i]) {
if (studentAvgerages[i] >= 90) {
return "A"
} else if ((studentAvgerages[i] >= 80) && (studentAvgerages[i] < 90)) {
return "B"
} else if ((studentAvgerages[i] >= 70) && (studentAvgerages[i] < 80)) {
return "C"
} else {
return "Failed"
}
}
}
console.log(evalGrades(studentAvgerages[0]))
console.log(evalGrades(studentAvgerages[1]))
console.log(evalGrades(studentAvgerages[2]))
Thought I knew what you were looking for, less sure now, but hope this helps a little, somehow? As others have shown, there are some one liners to arrive at your average, if that's what you want.
var studentGrades = [
[80, 90, 94],
[80, 90, 94],
[80, 90, 94]
]
for(var i=0; i < studentGrades.length; i++){
var avg = 0;
for(var j=0; j < studentGrades[i].length; j++){
avg += studentGrades[i][j];
}
avg = avg/studentGrades[i].length;
switch(true){
case (avg >= 90):
console.log("A");
break;
case (avg >= 80):
console.log("B");
break;
case (avg >= 70):
console.log("C");
break;
case (avg >= 60):
console.log("D");
break;
default:
"Failed";
break;
}
}
I prefer switch...case for tasks like this a lot of times, but don't forget to take into account performance. On an array of 20,000 sets of 200 student grades, might be worth using if/else to maintain speed of page. See this answer for more details.
You could take an exit early approach fro getting the grade. No else parts are necessary bycause of the return statement.
For getting the average, you could take a dynamic approach with adding values and divide by the length of the array.
const
add = (a, b) => a + b,
getAverage = array => array.reduce(add, 0) / array.length,
evalGrades = grade => {
if (grade >= 90) return "A";
if (grade >= 80) return "B";
if (grade >= 70) return "C";
return "Failed";
},
studentGrades = [[80, 90, 94], [80, 70, 60], [76, 82, 91]],
studentAvgerages = studentGrades.map(getAverage);
console.log(...studentAvgerages);
console.log(...studentAvgerages.map(evalGrades));
If you are new at programming or javascript, practice some basic examples first and try to understand how the code should be structured in a way you can manage and reuse it. Basically functional programming at least.
From what I understood from your code, you need something that can dynamically calculate the grades of the students.
I have re rewritten the code hope that helps. Also, try to debug the code on your own so as to figure out how the code flows.
var studentGrades = [
[80, 90, 94],
[80, 90, 94],
[80, 90, 94]
]
function evalGrades(grades) {
var sum = 0;
for(var i =0; i<grades.length; i++){
sum = sum + grades[i];
}
var avg = sum/grades.length;
if (avg >= 90) {
return "A"
} else if ((avg >= 80) && (avg < 90)) {
return "B"
} else if ((avg >= 70) && (avg < 80)) {
return "C"
} else {
return "Failed"
}
}
for (var i = 0; i < studentGrades.length; i++) {
console.log(evalGrades(studentGrades[i]))
}
Try this. I hope I've been helpful.
var studentGrades = [
[80, 90, 94],
[80, 90, 94],
[80, 90, 94]
]
for (var i = 0; i < studentGrades.length; i++) {
var average = studentGrades[i].reduce((a, b) => a + b, 0) / studentGrades[i].length;
if (average >= 90) { var result = "A" }
else if ( average >= 80 && average < 90 ) { var result = "B" }
else if ( average >= 70 && average < 80 ) { var result = "C" }
else { var result = "Failed" }
console.log(result);
}
I am very new to javascript. In my class, we wrote a code for a random number generator, but mine isn't working. I was wondering if someone could look at it and tell me what I have done wrong. I think my syntax is wrong on the loops, but can't be sure.
function lottoGen() {
var i = 0; //Variable for increment
var d = 0; //Variable for decrement
var arr2 = [0, 0, 0, 0, 0, 0]; //6 array values. Begin at 0
arr2[5] = Math.random(1, 26); //Choose random number for position 5 in array
while (i <= 4) { //Perform loop while i <= 4
arr2[i] = Math.random(1, 69);
d = i;
while (d !== 0 && d <= 4) {
d--;
if (arr2[i] === arr2[d]) {
i--;
}
i++;
}
}
document.getElementById("lotto").innerHTML = arr2; //Print the array
}
<p>Lottery Number Generator</p>
<form>
<button onclick="lottoGen()">Generate</button>
<p id="lotto"></p>
At the first iteration, i is 0 and therefore d is zero too, therefore this block:
while (d !== 0 && d <= 4) {
d--;
if (arr2[i] === arr2[d]) {
i--;
}
i++;
}
}
does not run as d is 0, therefore i doesn't get incremented, and you end up at an infinite loop. You actually want to always step through the array:
while (i <= 4) { //Perform loop while i <= 4
arr2[i] = Math.random(1, 69);
d = i;
while (d !== 0 && d <= 4) {
d--;
if (arr2[i] === arr2[d]) {
i--;
}
}
i++; // <<<
}
Additionally, Math.random() does not take any arguments and returns a number from 0 until 1, so to get an integer in a certain range you'd have to do use a small utility:
const random = (min, max) => min + Math.floor((max - min) * Math.random());
console.log(random(1, 69));
PS: To be honest, your code is actually quite hard to understand, and the comments aren't really helpful. Instead of describing the code, try to describe what you're trying to achieve there:
// Step through the array and fill it with random numbers
while (i <= 4) {
arr2[i] = random(0, 69);
d = i;
// Check all positions to the left if the number is already taken
while (d !== 0 && d <= 4) {
d--;
if (arr2[i] === arr2[d]) {
// If thats the case, stay at this position and genrate a new number
i--;
}
}
i++;
}
How I would write that:
function lottoGen() {
const result = [];
for(let count = 0; count < 6; count++) {
let rand;
do {
rand = random(0, 69);
} while(result.includes(random))
result.push(rand);
}
return result;
}
You are given an array (which will have a length of at least 3, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this "outlier" N
Example: [2, 4, 0, 100, 4, 11, 2602, 36]
Should return: 11 (the only odd number)
my sol:
function findOutlier(integers){
var odd = false;
var even = false;
if ((integers[0]%2===0) && (integers[1]%2===0)) || ((integers[1]%2===0) && (integers[2]%2===0)){
even = true;
}else{
odd = true;
}
if (odd){
for (var i = 0; i < integers.length; i++){
if (integers[i]%2 === 0){
return integers[i];
}}
}else if (even){
for (var i = 0; i < integers.length; i++){
if (integers[i]%2 !== 0){
return integers[i];
}}
}
}
Your first if-condition needs to be surrounded by parenthesis:
if a || b needs to be if (a || b). Then it works. :)
function findOutlier(integers) {
var odd = false;
var even = false;
if (((integers[0] % 2 === 0) && (integers[1] % 2 === 0)) || ((integers[1] % 2 === 0) && (integers[2] % 2 === 0))) {
even = true;
} else {
odd = true;
}
if (odd) {
for (var i = 0; i < integers.length; i++) {
if (integers[i] % 2 === 0) {
return integers[i];
}
}
} else if (even) {
for (var i = 0; i < integers.length; i++) {
if (integers[i] % 2 !== 0) {
return integers[i];
}
}
}
}
var result = findOutlier([2, 4, 0, 100, 4, 11, 2602, 36] );
console.log(result);
Here's another approach to solve that:
function findOutlier(integers) {
var outlier;
var odd = [];
var even = [];
// Push odd numbers to odd array and even numbers to even array.
integers.forEach(function(element) {
if (element % 2 == 0) even.push(element);
else odd.push(element);
});
// Ensure that the input is valid.
if (odd.length != 1 && even.length != 1) {
console.log("There is no single outlier! The array contains " + odd.length + " odd integers and " + even.length + " even integers.")
} else {
// Get outlier.
outlier = (odd.length == 1 ? odd[0] : even[0]);
}
return outlier;
}
var result = findOutlier([2, 4, 0, 100, 4, 11, 2602, 36]);
console.log(result);
Have a script that defines text as being a number using a regular expression:
function validateTextNumericInRange(textInputId)
{
var numRegex = /^[\d]+$/;
var textInput = document.getElementById(textInputId);
var valid = true;
if (numRegex.test(textInput.value) == false) {
alert('Value must be a number between 3-48');
valid = false;
}
return valid;
}
Need to use this format and provide a min/max range (arbitrary for now, say between 3 and 48). How would I modify this regex to complete and have a correct argument?
I don't understand your question. Do you mean that you want a number that is between 3 and 48 digits, or that the value of the number must be between 3 and 48?
For the latter you don't need a regex:
function validateTextNumericInRange (textInputId) {
var textInput = document.getElementById(textInputId);
var value = parseInt(textInput.value, 10);
return (!isNaN(value) && value >= 3 && value <= 48);
}
A more generic solution:
function validateTextNumericInRange(textInputId, min, max) {
var textInput = document.getElementById(textInputId);
var value = parseInt(textInput.value, 10);
return (!isNaN(value) && value >= min && value <= max);
}
To test to see if a number is between 3 and 48 digits long, you can use the regular expression /^[0-9]{3, 48}$/.
A regular expression would be hard and inflexible, and for your example it would be:
/^(?:[3-9]|[1-3][0-9]|4[0-8])$/
Better go with Vivins' solution.
I'm not a good JavaScript developer, but I know that in Java you can use this syntax to test for minimum and maximum length:
[1-9][0-9]{2,4}
[1-9][0-9]{min,max}
function isInteger(value) {
if ((value.toString()).replace(/^-\d|\d/, "").length == 0) {
return true;
}
return false;
}
function integerInRange(value, min, max) {
if (isInteger(value)) {
if (parseInt(value, 10) >= min && parseInt(value, 10 <= max)) {
return true;
} else {
return false; // Not in range
}
} else {
return false; // Not an integer
}
}
integerInRange( 55, 3, 48); // Returns false
integerInRange("55", 3, 48); // Returns false
integerInRange( 25, 3, 48); // Returns true
integerInRange("25", 3, 48); // Returns true
In your case you would need to call it this way:
integerInRange(document.getElementById(textInputId).value, 3, 48);