Tell us what’s happening:
I’ve spent a couple of days on this and I’ve caught many mistakes I had made that caused certain tests to fail, but my code still can’t pass them all. I think the functionality is there, so can’t figure out what’s missing. My code fails tests 10 - 13, 18 and 19. Any and all help is appreciated! Thank you!
My code so far
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<input id = "cash" placeholder = "Customer's cash"/>
<div id = "change-due"></div>
<button id = "purchase-btn">Purchase</button>
<script src="./script.js"></script>
</body>
</html>
let price = 11.95;
let cid = [
['PENNY', 10],
['NICKEL', 0],
['DIME', 0],
['QUARTER', 0],
['ONE', 0],
['FIVE', 0],
['TEN', 0],
['TWENTY', 0],
['ONE HUNDRED', 0]
];
//copy of cid that the transaction will be tested on before commited. I want to use cents instead of floating numbers to avoid JavaScript's issue with those. Also, I want the array reversed, since the process' logic is to first give out larger bills. I could leave it as is and make the loops i-- instead of i++, but I'd rather it fits better to the actual process and therefore any visualization someone reading this (or I) might need to do
let cidCopy = cid.map(([name, amount]) => [name, Math.round(amount * 100)]).reverse();
//again, I want cents for math
let priceInCents = price * 100;
//the reduce() array method, in this case, adds all the cid[i][1] elements to zero to get their sum. So cidTotal(cid) would be dollars and cidTotal(cidCopy) would be cents
const cidTotal = (register) => register.reduce((sum, [, amount]) => sum + amount, 0);
//shortcut to get DOM element by passing its id
const getEle = (id) => document.getElementById(id);
const cashInput = getEle('cash');
const purchaseButton = getEle('purchase-btn')
const changeDueBox = getEle('change-due');
//shortcut to set a DOM element's textContent
const setText = (elem, content) => {elem.textContent = content};
//shortcut to append to a DOM element's textContent
const appendText = (elem, content) => {elem.textContent += content};
//purchase button logic
const purchaseBtnClick = () => {
setText(changeDueBox, '');
//convert user's dollar input to cents for math
let userCashInCents = cashInput.value * 100;
//if the user pays less than the listed price, pop an alert up. The else if blocks don't execute and the method returns
if (userCashInCents < priceInCents)
{
//temp =====================================
console.log(`Insufficient customer cash`);
//temp =====================================
alert('Customer does not have enough money to purchase the item');
}
//if the user pays exactly the listed price, the changeDueBox reads the appropriate message (No change due - customer paid with exact cash). The following else if and else blocks don't execute and the method returns
else if (userCashInCents == priceInCents)
{
//temp =====================================
// console.log(`exact cash (\$${userCashInCents / 100}), cidTotal(cidCopy): ${cidTotal(cidCopy)} cents or ${cidTotal(cidCopy) / 100} dollars`);
//temp =====================================
setText(changeDueBox, 'No change due - customer paid with exact cash');
}
//if the user pays more than the listed price, the bulk of the logic executes to make out the change
else {
//temp =====================================
// console.log(`user paid: \$${userCashInCents / 100}, price is \$${priceInCents / 100}`)
// console.log(`cidTotal(cidCopy): ${cidTotal(cidCopy)} cents or ${cidTotal(cidCopy) / 100} dollars`);
//temp =====================================
//amount of cash to return in cents
let changeAmount = Math.round(userCashInCents - priceInCents);
//temp =====================================
// console.log(`changeAmount: ${changeAmount} cents or \$${changeAmount / 100}`);
//temp =====================================
//if more cash needs to be returned than is available in the register, the changeDueBox reads the appropriate message (Status: INSUFFICIENT_FUNDS). The else if blocks don't execute and the method returns
if (changeAmount > cidTotal(cidCopy))
{
//temp =====================================
// console.log('changeAmount > cidTotal(cidCopy) => INSUFFICIENT FUNDS');
//temp =====================================
setText(changeDueBox, 'Status: INSUFFICIENT_FUNDS');
}
//if the amount of cash that needs to be returned is exactly equal to the register's total, the changeDueBox reads the appropriate message (Status: CLOSED). The following else if block doesn't execute and the method returns
// else if (changeAmount == cidTotal(cidCopy)) //commented out this block because the tests' requirements changed
// {
// //temp =====================================
// // console.log('changeAmount = cidTotal(cidCopy) => register CLOSED');
// //temp =====================================
// setText(changeDueBox, 'Status: CLOSED')
// }
//if the amount of cash that needs to be returned is less than the register's total, calculations and logic ensue. To handle the case where the denominations are inadequate, the check first occurs with copies of the register and the changeAmount and then are applied over to the actual objects only when the transaction can complete
else if (changeAmount <= cidTotal(cidCopy)) //added the equal sign because I commented the above if check that set Status: CLOSED.
{
//the change array is just used to save the data somewhere, maybe for debugging or logging. It will hold pairs of (denomination, value)
let change = [];
//temporary copies of changeAmount, cidCopy and change that will be used to simulate the transaction. If it can complete successfully, they will replace their non-temporary counterparts. If not, their counterparts will remain untouched, the changeDueBox will read the appropriate message (Status: INSUFFICIENT_FUNDS) and the method will return. Note that for cidCopy I use .map() to copy the inner arrays' values as well. If I just did cidCopyTemp = cidCopy it would hold references to the inner arrays, so any changes I did would apply to the non-temporary counterpart as well
let changeAmountTemp = changeAmount;
let cidCopyTemp = cidCopy.map(inner => [...inner]);
let changeTemp = [];
//temp =====================================
// for (let i = 0; i < cidCopyTemp.length; i++)
// {
// console.log(`cidCopyTemp[${i}]: ${cidCopyTemp[i]}`)
// }
//temp =====================================
//need to use something like this to check which denomination needs to be returned, depending on the changeAmount. So if 120$ need to be returned, the ONE HUNDRED denomination is the first to be checked. Contrarily, if 30$ need to be returned, the ONE HUNDRED denomination needs to be skipped, as the TWENTY one is the most appropriate. Their values are in cents
const denominations = [
{name: 'ONE HUNDRED', value: 10000},
{name: 'TWENTY', value: 2000},
{name: 'TEN', value: 1000},
{name: 'FIVE', value: 500},
{name: 'ONE', value: 100},
{name: 'QUARTER', value: 25},
{name: 'DIME', value: 10},
{name: 'NICKEL', value: 5},
{name: 'PENNY', value: 1}
];
for (let i = 0; i < denominations.length; i++)
{
//example: for i = 0, this is {'ONE HUNDRED', 10000}
const {name, value} = denominations[i];
//reset the amountToGive to zero for each denomination. This will be used to track how much cash needs to be returned for each denomination
let amountToGive = 0;
//for each denomination, as long as at least one piece needs to be returned AND exists in the register, its value is deducted from the amount of change needed (changeAmountTemp) and the register (cidCopyTemp[i][1]) and added to the amount of cash of this denomination that's to be returned. As soon as either a smaller denomination is needed, or no more pieces of this denomination remain in the register, the while loop ends
while (changeAmountTemp >= value && cidCopyTemp[i][1] >= value) {
changeAmountTemp -= value;
cidCopyTemp[i][1] -= value;
amountToGive += value;
}
//the changeTemp array has an entry inserted, holding the name of the denomination currently being examined, along with the amount of this denomination that's to be returned. The amount is in cents.
if (amountToGive > 0)
{
changeTemp.push([name, amountToGive]);
}
}
//checks if change is still needed, after the register has given all that it could
if (changeAmountTemp > 0)
{
setText(changeDueBox, 'Status: INSUFFICIENT_FUNDS');
//temp ==================================
// console.log(`register doesn't have enough of the right denominations`);
//temp ==================================
}
//this will execute if the transaction can be completed successfully. For now, the changes have only been applied to the temporary variables. Those changes need to also be applied to their non-temporary counterparts
else {
if (cidTotal(cidCopyTemp) === 0)
{
setText(changeDueBox, 'Status: CLOSED');
}
else {
setText(changeDueBox, 'Status: OPEN');
}
for (let i = 0; i < changeTemp.length; i++)
{
//example where the first denomination that was appropriate to be returned is the 20$ bill and that two of them were needed: for i = 0, this is ['TWENTY', 4000]. The amount is in cents
const [name, amount] = changeTemp[i];
//temp ==================================
// console.log(`name - amount pair: ${name} - \$${amount / 100}`);
//temp ==================================
//each entry is added to the change array (so the changes to the temporary one are finally being copied to the non-temporary counterpart)
change.push([name, amount]);
//!! !! !! !!
//this needs to change. It changes each entry progressively. If not all denominations are present in the changeTemp array, this will alter wrong entries. For example, say the first denomination to be removed from the register is the $20 bill, for the first iteration of the loop, amount would be the amount of cash in $20 bills, but cidCopy[0][1] is the amount of cash in the register in the form of $100 bills.
// cidCopy[i][1] -= amount;
//!! !! !! !!
//fix: this uses the name of the denomination as it is inside the changeTemp array to get that denomination's index in the cidCopy array. Reminder: both use cents
/*personal note:
([denominationName]) => denominationName === name (array destructuring)
is the same as
denomination => denomination[0] === name
*/
const cidIndex = cidCopy.findIndex(([denominationName]) => denominationName === name);
if (cidIndex != -1)
{
cidCopy[cidIndex][1] -= amount;
}
appendText(changeDueBox, ` ${name}: \$${(amount / 100)}`);
}
//temp ==================================
// console.log(`register after successful transaction: ${cidTotal(cidCopy)} cents or ${cidTotal(cidCopy) / 100} dollars`);
//temp ==================================
}
}
}
return;
};
//event listener on the purchase button to run the purchaseBtnClick() method when clicked
purchaseButton.addEventListener("click", purchaseBtnClick);
//event listener on the input box to run the purchaseBtnClick() method when 'Enter' is pressed
// cashInput.addEventListener('keydown', e => e.key === 'Enter' && purchaseBtnClick());
/* file: styles.css */
Your browser information:
User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0
Challenge Information:
Build a Cash Register Project - Build a Cash Register