Why i keep getting NaN value in my tip calculator? - javascript

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.

Related

AddEventListener updating value for global scope

Introduction
I have select box for user to choose how much data he want to see in visualisation , this data is array of possitive unique numbers randomly choosen. I need to change this data length to put it into d3.js to drawVisualistion there
Problem
When i select some option from select box my application start laggy that i need to close the tab. This is the code i used.
import generateData from "./generateData";
const dataSizeSelect = document.getElementById(
"data-size"
) as HTMLSelectElement;
let data:number[] = [];
dataSizeSelect.addEventListener("change", () => {
const dataSize = parseInt(dataSizeSelect.value, 10);
data = generateData(dataSize)
});
console.log(data);
export {};
Then i tried in diffrent method but only gives zero , after some reading about how it is done in addEventListener function i read that it always be zero becouse code i have is asonchronus:
import generateData from "./generateData";
const dataSizeSelect = document.getElementById(
"data-size"
) as HTMLSelectElement;
let dataSize: number = 0;
dataSizeSelect.addEventListener("change", () => {
dataSize = parseInt(dataSizeSelect.value, 10);
});
const data = generateData(dataSize);
export {};
And this is the generateData func:
const generateData = (numPoints: number): number[] => {
const data = new Set();
while (data.size < numPoints) {
const randomNumber = Math.floor(Math.random() * 100);
if (randomNumber > 0) {
data.add(randomNumber);
}
}
return [...data] as number[];
};
export default generateData;

JavaScript not updating the DOM and crashing the browser

I have written the following HTML for a button that on click calls a function and the output of the function is written in a div in the DOM. But it is not updating the DOM anyway, but freezing the whole browser tab as well as the HTML page. Please help.
Thank you in advance
let rows = [];
let cols = [];
let secKey = "";
const generateKey = () => {
var count = 0;
while (count != 5) {
let randomNumber = Math.floor((Math.random() * 10));
if (!rows.includes(randomNumber)) {
rows.push(randomNumber);
count++;
}
}
count = 0;
while (count != 6) {
let randomNumber = Math.floor((Math.random() * 10));
if (!cols.includes(randomNumber)) {
cols.push(randomNumber);
count++;
}
}
// put on the document
secKey = `${cols[0]}${rows[0]}${cols[1]}${rows[1]}${cols[2]}${rows[2]}${cols[3]}${rows[3]}${cols[4]}${rows[4]}${cols[5]}`;
document.querySelector("#sec-key").innerHTML = `Your secret key is <strong id="sec-key">${secKey}</strong>`; // #sec-key is a div where I want to show the output
};
Html:
<div class="key-container">
<button id="generate-key" class="util-btn" onclick="generateKey()">Generate new secret key</button>
<p class="key-holder" id="sec-key">
<!--output is expected here-->
</p>
<p id="caution">*please remember the secret key for decryption</p>
</div>
The problem is that upon running the function a second time, the globals likely already include the random values, and so the count variable is never incremented and the loop spins infinitely.
Either initialize the globals inside the function implementation, or use the length of the array instead of a counter. Second approach is shown below:
let rows = [];
let cols = [];
let secKey = "";
const generateKey = () => {
while (rows.length != 5) {
let randomNumber = Math.floor((Math.random() * 10));
if (!rows.includes(randomNumber)) {
rows.push(randomNumber);
}
}
while (cols.length != 6) {
let randomNumber = Math.floor((Math.random() * 10));
if (!cols.includes(randomNumber)) {
cols.push(randomNumber);
}
}
// put on the document
secKey = `${cols[0]}${rows[0]}${cols[1]}${rows[1]}${cols[2]}${rows[2]}${cols[3]}${rows[3]}${cols[4]}${rows[4]}${cols[5]}`;
document.querySelector("#sec-key").innerHTML = `Your secret key is <strong id="sec-key">${secKey}</strong>`; // #sec-key is a div where I want to show the output
};

I want to find string sequence with rxjs

I want to find string sequence with rxjs
for example
Target string: 01234010
Match stringe: 01
answer = 2
I have a solution with using javascript
let index = 0;
while (true) {
let foundIndex = targetSequence.indexOf(matchSequence, index);
if (foundIndex == -1) break;
index = foundIndex + 1;
}
but the problem is that I have to use rxjs with those skeleton code
import { from, Observable } from "rxjs";
const targetSequence = `01234010`;
const matchSequence = "01";
const _target = from(targetSequence);
const _output: Observable<number> = _target.pipe(
// here is your code
);
_output.subscribe({
next: val => console.log(`count : ${val}`)
});
do you guys have any idea?
For this, you could use the rxJS operator map.
E.g.
const targetSequence = '01234010';
const matchSequence = '01';
const _target = of(targetSequence);
const _output = _target.pipe(
map((val) => {
let foundIndex = val.indexOf(matchSequence);
return foundIndex + matchSequence.length;
})
);
_output.subscribe({next: (val) => console.log(val)})
Note: I've used the of method to generate the observable, using from emitted for each character. I've also slightly modified your method of finding the result but I think it achieves what you want.

What is the best way to clean up the code?

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)
}

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;
}

Categories