What is the best way to clean up the code? - javascript

I have a simple function that has to give a change. Something like vending machine. It takes 2 arguments: price of the item and an array of bills and coins received. The output must be an array of numbers only in [quarter, dime, nickel, penny] format. For example, item costs 3.29 and the amount received is [1,1,2]. In this case the output must be [2,2,0,1] because the change which is 0.71 can be divided as 2 quarters, 2 dimes, 0 nickels and 1 penny. If amount received is less than a price then it has to return the full amount but only in format mentioned above. For example, if the price is 5 but amount paid is [2,2], the output must be [16,0,0,0]. I have created this function:
`function change(price,paid) {
const totalPaidVal = paid.reduce((a,b)=>a+b,0)
if (totalPaidVal === price) {
return(Array(4).fill(0))
} else if (price > totalPaidVal) {
const qNum = Math.floor(totalPaidVal/0.25);
const dNum = Math.floor((totalPaidVal-(qNum*0.25))/0.1);
const nNum = Math.floor((totalPaidVal-(qNum*0.25)-(dNum*0.1))/0.05);
const pNum = Math.round((totalPaidVal-(qNum*0.25)-(dNum*0.1)-(nNum*0.05))/0.01);
const arr = [qNum,dNum,nNum,pNum];
return arr;
} else if(price<totalPaidVal) {
const change = totalPaidVal-price;
const qNum = Math.floor(change/0.25);
const dNum = Math.floor((change-(qNum*0.25))/0.1);
const nNum = Math.floor((change-(qNum*0.25)-(dNum*0.1))/0.05);
const pNum = Math.round((change-(qNum*0.25)-(dNum*0.1)-(nNum*0.05))/0.01);
const arr = [qNum,dNum,nNum,pNum];
return arr;
}
}`
It works fine but I know that it looks like I repeat myself over again what is not good. I've been trying to create other variables to clean it up but it still looks pretty weird. There must be more efficient way to do that without repeating same pieces of code all the time. So, here is the question: what is the best way to clean it up and make it look simpler than now?

You can use a function to remove the duplicity from code.
function getNums(value){
const qNum = Math.floor(value/0.25);
const dNum = Math.floor((value-(qNum*0.25))/0.1);
const nNum = Math.floor((value-(qNum*0.25)-(dNum*0.1))/0.05);
const pNum = Math.round((value-(qNum*0.25)-(dNum*0.1)-(nNum*0.05))/0.01);
return [qNum,dNum,nNum,pNum];
}
call this function based on condition
if (price > totalPaidVal) {
return getNums(totalPaidVal)
} else if(price < totalPaidVal){
return getNums(totalPaidVal - price)
}

Related

Is there any way i can limit this for loop to a maximum of 50 loops?

This is a really unusual code i haven't written myself, each day a database is updated with around 200 records.
This means we have to do 200 api calls to figure out infomation about theese records every day at 7 am.
This has been working fine until now,
the api has implemented a limit of 50 api calls pr hour, and IF you try to do 51 calls, you get banned for 24 hours...
SO, how do i make the for loop do a maximum of 50 loops here?
for (let record of records ) {
//This loop has to be limited to 50 iterations pr run.
let brregRawData = await fetch(`some url/${record.name}/someurl`);
let brregJson = await brregRawData.json()
let personNavn = brregJson.rollegrupper[0].roller[0].person.navn.fornavn
let personEtternavn = brregJson.rollegrupper[0].roller[0].person.navn.etternavn
// if mellomnavn = undefined, then do nothing
if (brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn == undefined) {
var personMellomNavn = ""
} else {
let personMellomNavn = brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn + " "
}
I tried looking up different ways to use a for loop, but all the solutions i looked at broke the "${record.name}" part of the url, i have to loop through an entire view of an entire database.
I'm not sure if this is right for the case, but can you define a counter outside of the loop, increment internally and break when it reaches 50.
let count = 0;
for (....)
...
if (count++ > 50) break;
...
If I'm understanding you correctly, you need to limit the number of api calls to 50 but the current implementation uses a for ... of loop. The simplest way to achieve what you're looking for with the least amount of modification is to use a standard for loop.
Assuming this all happens inside an async function...
async function limitToFiftyApiCalls(records) {
const max = Math.min(records.length, 50);
for (let i = 0; i < max; i++) {
const record = records[i];
let brregRawData = await fetch(`some url/${record.name}/someurl`);
let brregJson = await brregRawData.json();
let personNavn = brregJson.rollegrupper[0].roller[0].person.navn.fornavn;
let personEtternavn = brregJson.rollegrupper[0].roller[0].person.navn.etternavn;
// if mellomnavn = undefined, then do nothing
if (brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn == undefined) {
var personMellomNavn = "";
} else {
let personMellomNavn = brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn + " ";
}
}
}
The code above doesn't modify your existing code much other than limiting the number of API calls. However there's a few things you could do that would generally make the code easier to read and edit.
async function limitToFiftyApiCalls(records) {
const max = Math.min(records.length, 50);
for (let i = 0; i < max; i++) {
const record = records[i];
let personMellomNavn = "";
let brregRawData = await fetch(`some url/${record.name}/someurl`);
let brregJson = await brregRawData.json();
// why write this more than once?
// save it to a variable and make life easier
let someVar = brregJson.rollegrupper[0].roller[0].person.navn;
let personNavn = someVar.fornavn;
let personEtternavn = someVar.etternavn;
if (someVar.mellomnavn) {
personMellomNavn = someVar.mellomnavn + ' '
}
}
}
A very simple way to do this is implementing a count that increments every time the loop executes the body. So for the the loop you provided it would look like this:
let count = 0; // initialize count as 0
for (let record of records ) {
if(count >= 50) break; // break out of loop if count is equal to or greater than 50 (since count starts from 0)
//This loop has to be limited to 50 iterations pr run.
let brregRawData = await fetch(`some_url/${record.name}/some_url`);
let brregJson = await brregRawData.json()
let personNavn = brregJson.rollegrupper[0].roller[0].person.navn.fornavn
let personEtternavn = brregJson.rollegrupper[0].roller[0].person.navn.etternavn
// if mellomnavn = undefined, then do nothing
if (brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn == undefined) {
var personMellomNavn = ""
} else {
let personMellomNavn = brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn + " "
}
count++; // Increment the count after each iteration
}
Answer to you question:
for (let i=0; i<math.min(records.length, 50); i++ ) {
let record = records[i];
But what happens then with the records you have not checked? Will you wait 24h? I guess that's not what's expected, and instead will have to check 50 records every hour until you have checked them all (so 4 times for 200 records).

Why i keep getting NaN value in my tip calculator?

I can't find out what is going wrong in my code. Thank you.
I have attached the link to code via codepen.
https://codepen.io/tenzin12/pen/rNmmPbv
`const confirmBtn = document.querySelector(".confirm");
const tipField = document.querySelector(".p1");
const totalField = document.querySelector(".p2");
const tipPercentage = document.querySelector("#tip").children;
const customTip = document.querySelector(".custom").value;
const inputAmt = document.querySelector("#amount").value;
const totalPerson = document.querySelector(".number_of_people").value;
const calcFunction = (bill, percent, diners) => {
const percentage = percent / 100;
const tipPerPerson = (bill * percentage) / diners;
const finalBillPerPerson = bill / diners;
const finalWithTip = finalBillPerPerson + tipPerPerson;
tipField.textContent = tipPerPerson;
totalField.textContent = finalWithTip;
};
for (let i = 0; i < tipPercentage.length; i++) {
tipPercentage[i].addEventListener("click", () => {
if (parseInt(totalPerson) > 0) {
if (tipPercentage[i].value.toUpperCase() === "CUSTOM") {
calcFunction(parseFloat(inputAmt), parseInt(customTip), parseInt(totalPerson));
}
}
calcFunction(parseFloat(inputAmt), parseInt(tipPercentage[i].value), parseInt(totalPerson));
});
}
`
When you need to run calculations on element values, you need to collect those values at the time of calculation. You were collecting them up front - but then when you were calculating the function, it was using those old values. I moved those into your function. Note how I got rid of most of the parseInt and parseFloat functions in favor of the minimal + operator which does the same thing.
Additionally, I simplified the code a little and put in a validation to prevent totals being run on 0 people or 0 amounts. Finally, I changed your for loop into an HTMLCollection forEach loop. I find it is easier to read and maintain
const confirmBtn = document.querySelector(".confirm");
const tipField = document.querySelector(".p1");
const totalField = document.querySelector(".p2");
const tipPercButtons = document.querySelectorAll("#tip input.percentage");
const calcFunction = (bill, percent, diners) => {
const percentage = percent / 100;
const tipPerPerson = (bill * percentage) / diners;
const finalBillPerPerson = bill / diners;
const finalWithTip = finalBillPerPerson + tipPerPerson;
tipField.textContent = tipPerPerson;
totalField.textContent = finalWithTip;
};
tipPercButtons.forEach((el) =>
el.addEventListener("click", (e) => {
const customTip = +document.querySelector(".custom").value;
const inputAmt = +document.querySelector("#amount").value;
const totalPerson = +document.querySelector(".number_of_people").value;
if (isNaN(totalPerson) || isNaN(inputAmt)) {
alert("Please designate the number of people and the amount of the bill")
return;
}
if (totalPerson === 0) return;
let val
if (e.target.value.toUpperCase() === "CUSTOM") val = customTip;
else val = parseInt(e.target.value);
calcFunction(inputAmt, val, totalPerson);
})
);
Updated pen: https://codepen.io/john-tyner/pen/MWmmLMQ?editors=1111
i analysed your code there is some error in fetching the input value in the code.
below is the correct code. Hope this might work
make the following little changes in your code:
const inputAmt = document.querySelector("#amount");
const totalPerson = document.querySelector(".number_of_people");
and this at the bottom outside the if block
calcFunction(
parseFloat(inputAmt.value),
parseInt(tipPercentage[i].value),
parseInt(totalPerson.value)
);
overall your calculator is So interesting.

How to fix Euclidean Distance function? (Movie Recommender)

Something similar to this https://blazingedge.io/blog/movie-recommendation-javascript/
My ecDistance function is getting small similarity scores
For example: .01 - 0059 and smaller. The logic is correct. What I think may be wrong is looping through all of the data and using it in user2 to compare each one in a loop.
ecDistance Function takes in user1 which is from the command line or
process.argv[2] and user2 all the of the movies.'
user2:
//object
[{
userId: 1
movieId:1
rating:4.5
}, ...]
function ecDistance(user1, user2) {
let objRating = getComparedRating(user1, user2) // function that handles all the ratings that are the same from both users
let totoalOfSums = 0
let tempSim = []
//user2 is all of the users besides user one [{ userId:1 , movieId:1, rating:4.5}]
for (let i = 0; i < user1.length; i++) { // user1 is what is taken in from process.argv[2] {
let diff = objRating.user1[i] - objRating.user2[i]
let squared = diff * diff
totalOfSums += squared
let squareRootSum = Math.sqrt(totalofSums)
let similarity = 1 / (1 + squareRootSum) // get the similarity score
tempSim.push(similarity)
}
}
function getComparedRating(user1, user2) {
let userRating1 = []
let userRating2 = []
user2.forEach((data) => {
user1.forEach((main) => {
/*
if //they have the same movieId and if they are not the same user,
push them in two different variables to use later on.
*/
if (data.movieId == main.movieId && data.userId !== main.userId) {
userRating1.push(main.rating)
userRating2.push(data.rating)
}
})
})
}
return {
user1: userRating1,
user2: userRating2
}
my expected Result are values, less than or equal to 1. (depending on the rating)

Getting previous objects value in current object

I am using a dataset that contains around 65k data. I am mapping over the dataset multiple times to massage the dataset. After obtaining the dataset in the required format, I am using map to do some computations with the price of the current item. But, whenever I return the current object, it contains the computation data of the previous object.
Whenever I log the data, it always shows the current object and the computations based on the current object. But, the returned object contains a previous object's data. Here is the route:
const {priceBands} = require('../utils/profitComputations');
let profitArray = [];
//calculating the price bands
profitArray = _.map(nfoArray, item=>{
console.log(item.cmp);
//returns the current market price; getting the correct value here
let priceBandVar = priceBands(Number(item.cmp));
console.log(priceBandVar);
//applying some algorithms; getting the correct value here
return {item: item.cmp, profitBand: priceBandVar};
//Here I find a mismatch between the parameter and the calculations
});
Here is the priceBands function in my 'utils/profitComputations':
const _ = require('lodash');
const priceBandInterval = {'-4':0, '-3':0, '-2':0, '-1':0, '0floor':0,'0ceil':0,'1':0, '2':0, '3':0, '4':0};
let priceBands = {};
module.exports = {
priceBands: function(price){
let factor = 0;
if(price>=10000){
factor = 100;
}else if (price>=1000 && price<10000){
factor = 50;
}else if (price>=500 && price<1000){
factor = 25;
}else if (price>=100 && price<500){
factor = 10;
}else if(price>=25 && price<100){
factor = 2;
}else{
factor = 0.5;
}
let priceCeil, priceFloor;
if((price%factor) == 0){
priceCeil = price + factor;
priceFloor = price - factor;
} else {
const remainder = price%factor;
priceCeil = price - remainder + factor;
priceFloor = price - remainder;
}
_.map(Object.keys(priceBandInterval), item=>{
if(parseInt(item)>0){
priceBands[item] = (parseInt(item)*factor) + priceCeil;
} else if (parseInt(item)<0){
priceBands[item] = (parseInt(item)*factor) + priceFloor;
} else {
priceBands['0floor'] = priceFloor;
priceBands['0ceil'] = priceCeil;
}
});
return priceBands;
}
}
I would appreciate if someone can share some valuable insights on what I am missing.
You must clone the variable priceBandVar because javaScript variables are called by reference. The following code is your answer:
profitArray = _.map(nfoArray, item => {
console.log(item.cmp);
//returns the current market price; getting the correct value here
let priceBandVar = priceBands(Number(item.cmp));
console.log(priceBandVar);
//applying some algorithms; getting the correct value here
return {
item: item.cmp,
profitBand: clone(priceBandVar)
};
//Here I find a mismatch between the parameter and the calculations
});
function clone(o) {
var ret = {};
Object.keys(o).forEach(function(val) {
ret[val] = o[val];
});
return ret;
}

Trying to add a conditional to a function, and receiving an error on execution

So, first and foremost, it's important to note that I'm adding a feature to something I didn't design. I'm really new to JavaScript, and I'm trying to edit an existing Discord bot. I discovered that the simplest way to achieve my goal would be to edit the root function at which it generates Random numbers. The relavant snippet of the original code (taken from the dice-expression-evaluator module https://github.com/dbkang/dice-expression-evaluator) is as follows:
Dice.prototype.roll = function () {
var rolls = [];
var sum = 0;
for (var i = 0; i < this.diceCount; i++) {
var roll = random.integer(1, this.sideCount) * this.coefficient;
rolls.push(roll);
sum += roll;
}
return {roll: sum, dice: rolls};
};
This original code works just fine, but doesn't include my desired feature (a simple-but-verbose sort of whitelist.) the 4 variables not defined in that particular code block are rather self-explanatory. My version of the code (slightly edited for privacy reasons) is as follows:
Dice.prototype.roll = function () {
var rolls = [];
var sum = 0;
var range = this.whitelist(); //already tried it with () after whitelist
console.log(range.join(','));
for (var i = 0; i < this.diceCount; i++) {
var roll = random.integer(range[0], range[1]) * this.coefficient; //changed the 2 function arguments, but both are still integers
rolls.push(roll);
sum += roll;
}
return {roll: sum, dice: rolls};
};
Dice.prototype.whitelist = function () {
let user0 = "a";
let user1 = "b";
let user2 = "c";
let user3 = "d";
let user = message.author.id;
let die = this.sideCount;
console.log(user);
console.log(string(die));
if (user==user0) {
var min = Math.ceil(0.76 * die);
var max = die;
} else if (user==user1) {
var min = Math.ceil(0.76 * die);
var max = die;
} else if (user==user2) {
var min = 1;
var max = die;
} else if (user==user3) {
var min = 1;
var max = die;
} else {
var min = 1;
var max = die;
}
return [min, max];
};
The message.author.id variable is available to the function that started the whole function chain 3 scopes up, but in MY version of the code, (even after correcting a few missing semicolons and similarly minute errors) a dice expression that is perfectly functional in the original code generates an "invalid dice expression" error. Other than the introduction of a new variable and the variables in the random.integer call, I see no functional difference between the old and new versions of Dice.prototype.roll. By my understanding, my whitelist function returns an array of integers, and those integers are being injected directly into the random.integer function call in a perfectly reasonable way... I am incredibly confused.

Categories