I've been going over this question now for a couple of days and I'm still no closer to getting it right or understanding as to how to get it to run properly.
This is the current code I have:
let waterPay = prompt("Please enter the amount of water you use to get a price you need to pay, thank you!");
if (waterPay < 6000) {
console.log("The number is below 6000");
console.log (waterPay / 1000); //The outcome of this must be saved as a different let
console.log (waterPay * 15.73);// outcome of the above times by this amount
}
else if (waterPay > 6000 && waterPay <= 10500) {
console.log("The number is between 6000 and 10500");
}
else if (waterPay > 10500 && waterPay <= 35000) {
console.log("The number is between 10500 and 35000");
}
else if (waterPay > 35000) {
console.log("The number is above 35000");
}
What my code needs to do is take an input from the user stating how many litres of water they use, you can see in the code that depending on the amount of litres they use it should print out how much they owe.
The table above states that the first 6 000 litres will cost R15.73 per kilolitre.
Next, water consumption above 6 000 litres but below 10 500 litres will be
charged at R22.38 per kilolitre. Therefore, a household that has used 8000
litres will pay R139.14 (15.73 x 6 + 22.38 x 2). The table carries on in this
manner.
Im battling to figure out how I should go about working this out. Any help would be appreciated.
The data structure needed is something that pairs rates with usage thresholds. The last threshold is effectively infinite, to catch any usage above the highest. The logic is to find() the right rate object and multiply that rate tier's rate by the usage.
let rateData = [{
upTo: 6000,
rate: 15.73
},
{
upTo: 10500,
rate: 22.38
},
{
upTo: 35000,
rate: 34.0. // made this one up, not in the OP
},
{
upTo: Number.MAX_SAFE_INTEGER,
rate: 50.0. // made this one up, not in the OP
}
];
function rateDatumForUsage(usage) {
return rateData.find(r => usage <= r.upTo);
}
function costForUsage(usage) {
const rateDatum = rateDatumForUsage(usage);
return usage * rateDatum.rate;
}
console.log(`The cost of using 5000 units is (15.73*5000) ${costForUsage(5000)}`)
console.log(`The cost of using 10000 units is (22.38*10000) ${costForUsage(10000)}`)
console.log(`The cost of using 100000 units is (50*100000) ${costForUsage(100000)}`)
Total cost should be calculated by steps.
This means that, for example, if the first 10 liters cost USD 2, the following 10 liters (from 10 to 20) cost USD 1 and from 20 cost will be USD 0.5, then the total cost for 30 liters will be: 10*2 + 10*1 + 10*0.5 = 35.
This can only be achieved generically by looping. Here is the code:
const steps = [
6000,
10500,
35000
];
const rates = [
10,
20,
30
];
function calculate(used) {
let output = 0;
for (let i = 0; i < steps.length; i++) {
if (used >= steps[i]) {
output += steps[i] * rates[i];
} else {
output += (used - (steps[i - 1] || 0)) * rates[i];
break;
}
}
return output;
}
console.log(calculate(3000));
console.log(calculate(6000));
console.log(calculate(9000));
console.log(calculate(50000));
Related
i got a small headache over a problem regarding an efficient way to distribute a fixed amount of points evenly between n items that got different costs and limits. Costs don't increase with each item.
Lets say i got 3 Items:
Name
Cost
Limit
A
25
220
B
30
20
C
50
60
Further we got fixed Points: 5000.
I want to know how many times i can buy each.
My curent solution runs a loop and deducts the cost from points until either all limits are reached or points ran out.
http://jsfiddle.net/nasc8rfL/
var points = 5000;
var costA = 25;
var costB = 30;
...
var limitA = 220;
...
var maxA = 0;
while (points > 0){
if (points >= costA && limitA > 0){
points -=costA;
limitA -=1;
maxA +=1;
};
if (points >= costB && limitB > 0){
points -=costB;
limitB -=1;
maxB +=1;
};
if (points >= costC && limitC > 0){
points -=costC;
limitC -=1;
maxC +=1;
};
if((points < costA) and (points < costB) and (points <costC)) break;
}
console.log(maxA,maxB,maxC);
Eventually it will not stay at A,B,C but variable number of elements(not more than 20) so i'd loop through each element instead of 3 IFs.
I dont actually have to deduct points i just have to know how many of each item can be bought. I feel like im missing something and there is an easier way to determine the number of each.
I've thought about weighting them based on their limits but my head doesnt want to work with me and im pretty stuck right now.
In addition im a beginner in javascript, so if you guys got some tips to get the shown loop faster or more convinient maybe with something like
function func(arr){
arr.forEach(x=>{doSomething();})
i would be more than happy.
Your approach isn't bad. The algorithm can be sped up a bit by
keeping the elements in a list, and not just visiting each during each iteration of your outer loop, but by kicking it out of the list once it hit its limit
not just buying one of each item per round, but as many as feasible if you'd by equally many of each while staying within budget.
const points = 5000;
const items = [
{name: "A", cost: 25, limit: 220},
{name: "B", cost: 30, limit: 60},
{name: "C", cost: 50, limit: 20},
];
let amounts = Object.fromEntries(items.map(i => [i.name, 0]));
let available = items;
let toSpend = points;
do {
available = available.filter(i => amounts[i.name] < i.limit && i.cost <= toSpend);
const maxAmount = Math.max(1, Math.floor(toSpend / available.reduce((s, i) => s+i.cost, 0)));
for (const a of available) {
const amount = Math.min(maxAmount, a.limit - amounts[a.name]);
if (amount * a.cost <= toSpend) {
amounts[a.name] += amount;
toSpend -= amount * a.cost;
} else {
break;
}
}
} while (available.length);
console.log(amounts);
console.log(`Leftover: ${toSpend}`);
I have the following code:
function display_message() {
var low = data.result[0].max; //returns 30
var medium = data.result[1].max; // returns 60
var high = data.result[2].max; // returns 100
// mypoints are 67 for example
if(mypoints > low) {
if(mypoints > medium) {
alert('You got a high score');
} else {
alert('You got a medium score');
}
} else {
alert('You got a low score');
}
}
This code works fine. I compare my average score to the standard low / medium / high score.
Low score: 0-30 points
Medium score: 31-60 points
High score: 61-100 points
My question though is how to make my code a bit prettier? I am not sure if the code is considered as clear and efficient.
Any opinions would be much appreciated, thank you
There is no need for the if else with low, just check from smallest to highest.
if (mypoints <= low) {
//low msg
} else if (mypoints <= medium) {
//medium msg
} else {
//high msg
}
or you can go the opposite direction and check for the highest first with greater than
You could use a condition without nested conditions.
if (mypoints > medium) {
alert('You got a high score');
} else if (mypoints > low) {
alert('You got a medium score');
} else {
alert('You got a low score');
}
Here, we iterate over the various values that make up the score range. The loop will iterate over each score range in turn, meaning you need to have the lowest score first and highest score last. We then save score name against myscore to be alerted out at a later point.
This approach allows for expandability - you can add as many score ranges in the middle without having to add any more if/else blocks.
let data = {result: [{max: 30}, {max: 60}, {max: 100}]},
mypoints = 67;
function display_message() {
let score_range = {
low: data.result[0].max, //returns 30
medium: data.result[1].max, // returns 60
high: data.result[2].max // returns 100
},
myscore = 'fail';
for (var score in score_range) {
if (score_range.hasOwnProperty(score)) {
if (mypoints > score_range[score]) {
myscore = score;
}
}
}
alert('You got a ' + myscore + ' score!');
}
display_message();
You could store the messages in an array, and find the correct index like so:
function display_message() {
var low = 30,
medium = 60,
rank;
mypoints = 67; // for example
rank = ['low', 'medium', 'high'][+(mypoints > low) + +(mypoints > medium)];
console.log('You got a ' + rank + ' score');
}
display_message();
The magic is in the unary plus operator which converts booleans returned by comparisons to 0 or 1 accordingly. It's also easy to include more rankings if needed.
mypoints < low ? alert("you get low score") : (mypoints < medium ? alert("you get medium score") : alert("you get high score"))
You can use the switch statement
function display_message() {
var low = data.result[0].max; //returns 30
var medium = data.result[1].max; // returns 60
var high = data.result[2].max; // returns 100
switch (true) {
case mypoints > medium:
alert('You got a high score');
break;
case mypoints > low:
alert('You got a medium score');
break;
default:
alert('You got a low score');
}
}
You can create a function which takes the score and an array as an argument with the different levels and their names {"score": 30, "text": "You got a low score"} and then just loop that and output what is closest to what you sent in and return the matching text.
Example:
var myScore = 50,
scoreIntervals = [{
"score": 30,
"text": "Low score"
},{
"score": 60,
"text": "Average score"
},{
"score": 100,
"text": "High score"
}];
function evaluateScore(score, scoreIntervals) {
var output = scoreIntervals[scoreIntervals.length - 1].text;
$.each(scoreIntervals, function(key, val) {
if(score <= val.score) {
output = val.text;
return false;
}
});
return output;
}
console.log(evaluateScore(myScore, scoreIntervals));
I would want to select a random winner from about 2 - 10 players.
Every player have precent chance to win. Someone have 50% and someone 10%.
Let's say we have 2 players. One player have 20% and other have 80%. How do I select winner between these two?.
Players are in array
var players = {
player1: {
chance: 20 //%
}
player2: {
chance: 80 //%
}
}
//Select winner from json
(Assuming the percentages all add up to 100)
You would first have to order the players. Then take a random number from 1 to 100, and find out which player that random number falls under.
For example:
// Modified json to array so we can easily loop through them
// If you would like help turning the json to an array, I can provide code for that upon request
var players = [
{
chance: 20
},
{
chance: 40
},
{
chance: 40
}
];
// Generate random number
var perc = Math.random() * 100; // between 0 and 99.999~
// Save where we are in the percentage
var currentPerc = 0;
// Loop through the players and check who the random number chose
for ( var pID = 0; pID < players.length; pID++ ) {
// Check if the current player we are looking at has won
if (perc < (players[pID].chance + currentPerc)) {
alert("PLAYER " + (pID + 1) + " HAS WON.");
// Do player winning code here
break; // break out of the loop, we're done
} else {
currentPerc += players[pID].chance;
}
}
In the above example, imagine that the random number chose 45 (0.45 * 100 since math.random gives us 0.0 to 0.99~).
This would mean that player 2 won
0 to 20 = Player 1 wins
21 to 60 = Player 2 wins
61 to 100 = Player 3 wins
Using 45 as the random number chosen The first iteration, we check if player 1 has won. He has not, so we add player 1's percentage to the "current percentage".
Then in the second iteration we check player 2. Since 45 < (20 + 40), player 2 has won chosen. We alert that he has won and will do some code for that.
var players = [20,5,15,40,20];
var getWinner = function(players){
var random = Math.random();
var sum = 0;
for(var i = 0; i < players.length; i++){
sum+= players[i]/100;
if(random<= sum) return i;
}
}
Returns the number of the player(index 0) who wins
I need assistance creating a javascript function that performs the below logic.
If cubic ft is between 2500-5000 invoice $42, but if greater than 5000 then invoice $42 plus $22 per 1000 cubic ft.
Here is what I have so far:
var assessfee = function(cubicft) {
if (cubicft > 2500 || cubicft <= 5000) {
console.log($42);
} else if (cubicft > 5000) {
var diff = cubicft - 5000;
}
}
Something like this? Basically that sets the price to 42 since that is the default price and if cubicft is > 5000 it takes that divided by 1000 then takes it times 22 and adds that value to price. Finally it prints it to the console.
var assessfee = function(cubicft) {
var price = 42;
if (cubicft > 5000) {
price += (cubicft-5000)/1000*22;
}
console.log("$" + price);
}
I have the following array with two objects:
var myArr = [{
id: 3,
licences: 100
new_value_pr_licence: 40
}, {
id: 4,
licences: 200
new_value_pr_licence: 25
}]
A user wish to buy 150 licences. This means that they fall into the category 100 because they are above 100 licences but below 200 which means they pay $40 per licence.
Note that the array object values varies.
Order your plans by the price per licence:
myArr.sort(function (a, b) {
return a.new_value_pr_licence - b.new_value_pr_licence;
})
then starting from the start of the array, take as many of that plan as you can without going over the number the user wants to buy:
var numUserWants = 150;
var purchases = {};
var cheapestAvailableProduct = myArr.shift();
while (numUserWants > 0 && cheapestAvailableProduct) {
if (numUserWants <= cheapestAvailableProduct.licences) {
purchases[cheapestAvailableProduct.id] = Math.floor(cheapestAvailableProduct.licences / numUserWants);
numUserWants = cheapestAvailableProduct.licences % numUserWants;
}
cheapestAvailableProduct = myArr.shift();
}
At this point, purchases will now be a map of plan id to number:
purchases => {
3: 3
4: 1
}
This doesn't handle the case where over-purchasing is the cheapest option (eg: it's cheaper to buy 160 at 4x40, instead of 150 at 3x40 + 1x25 + 1x5), but it's probably a good start for you to tweaking.
Just a simple forEach here. Take the number requested, begin calculating/mutating total based on option limits, and once the number requested is less than the option limit you have your final total, which wont be mutated any longer and returned from the function.
function calculateDiscountedTotal(numberRequested, myArr){
var total;
// loop, compare, calculate
myArr.forEach(function(option) {
if(numberRequested >= option.licenses){
total = numberRequested * option.new_value_pr_licence
}
}
if(total != undefined){
return total;
} else {
// user never had enough for initial discount
return "no discount price";
}
}
Sort the array first in terms of number of licenses and then get the object in which number of licenses is less than number of licenses to be bought (just less than the next item in the array which is greater than number of licenses to be bought)
var myArr = [
{
id: 3,
licences: 100
new_value_pr_licence: 40,
},
{
id: 4,
licences: 200,
new_value_pr_licence: 25
},
];
var numOfLic = 150;
myArr.sort( function(a,b){ return a.licences - b.licences } );
var selectedObj = myArr.reduce( function(prev,current){
if ( current.licences > numOfLic )
{
return prev;
}
});
console.log ( "pricing should be " + ( selectedObj.new_value_pr_licence * numOfLic ) );