Build a Cash Register Project - Build a Cash Register

Tell us what’s happening:

It is not passing me because of test 14-19. I have to manually change the cid and price var to past some of the test but when I change it, the previous tests will fail. I have declared the 2 var with let.

Your code so far

<!-- file: index.html -->

/* file: script.js */

/* file: styles.css */

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36

Challenge Information:

Build a Cash Register Project - Build a Cash Register

Welcome to the forum @relzarick

So the forum can assist please post your full code.

Use the following method to post code to the forum:

  1. On a separate line type three back ticks.
  2. On a separate line paste your code.
  3. On the last line type three back ticks. Here is a single back tick `

Happy coding

let price = 1.87;

let cid = [
  ["PENNY", 1.01],
  ["NICKEL", 2.05],
  ["DIME", 3.1],
  ["QUARTER", 4.25],
  ["ONE", 90],
  ["FIVE", 55],
  ["TEN", 20],
  ["TWENTY", 60],
  ["ONE HUNDRED", 100],
];

const purchaseBtn = document.getElementById("purchase-btn");
const userCash = document.getElementById("cash");
const changeDisplay = document.getElementById("change-due");
const drawerDisplay = document.getElementById("change-container");
// console.log(cid);

let changeDisplayDenominations = [];
let assignDenominations = {};

let changeOnHand =
  Number(cid.reduce((acc, curVal) => acc + curVal[1], 0).toFixed(2)) * 100;
//console.log(changeOnHand);

let arrayOrderReversed = cid.map((arr) => Math.round(arr[1] * 100)).reverse();
const centsArray = [10000, 2000, 1000, 500, 100, 25, 10, 5, 1];
//console.log(arrayOrderReversed);

const check = () => {
  const cashGiven = Math.round(Number(userCash.value) * 100);
  const priceInCents = Math.round(price * 100);
  const changeDue = cashGiven - priceInCents;

  if (cashGiven < priceInCents) {
    alert("Customer does not have enough money to purchase the item");
  } else if (cashGiven === priceInCents) {
    cal(changeDue, cashGiven);
    updateUi(0);
  } else if (changeDue > changeOnHand || !checkDenom(changeDue)) {
    updateUi(1);
  } else {
    cal(changeDue, cashGiven);
    updateUi(2);
  }
};

const checkDenom = (changeDue) => {
  // * used to check the array without modifying the original
  const tempArrayOrderReversed = [...arrayOrderReversed];

  for (let i = 0; i < tempArrayOrderReversed.length; i++) {
    // * the second condition checks if current arrOrderReversed has enough value to match centsArray
    while (
      changeDue >= centsArray[i] &&
      tempArrayOrderReversed[i] >= centsArray[i]
    ) {
      // * still have to simulate deductions if it returns true it will proceed to next else block
      changeDue -= centsArray[i];
      tempArrayOrderReversed[i] -= centsArray[i];
    }
  }
  return changeDue === 0;
};

const cal = (change, cashGiven) => {
  let noChange = true;
  changeDisplayDenominations = [];
  // * assume that no changes will be made unless proven otherwise within the iteration

  if (cashGiven === 187) {
    
    while (cashGiven > 0) {
      noChange = true;
      for (let i = 0; i < centsArray.length; i++) {
        if (
          cashGiven >= centsArray[i] &&
          cid[cid.length - 1 - i][1] * 100 >= centsArray[i]
        ) {
          cashGiven -= centsArray[i];
          updateCid(i, centsArray[i]);
          changeDisplayDenominations.push(centsArray[i]);
          noChange = false;
          break;
        }
      }
      if (noChange) {
        break;
      }
    }
  }

  while (change > 0) {
    noChange = true;
    for (let i = 0; i < centsArray.length; i++) {
      if (
        change >= centsArray[i] &&
        cid[cid.length - 1 - i][1] * 100 >= centsArray[i]
      ) {
        change -= centsArray[i];
        updateCid(i, centsArray[i]);
        // * break is used to exit the innder for loop first, restarting the while loop
        // * used to exit early after highest val is deducted, ensuring highest value is deducted first
        changeDisplayDenominations.push(centsArray[i]);
        //console.log(changeDisplayDenominations);

        noChange = false;
        break;
      }
    }
    if (noChange) {
      break;
    }
  }

  //console.log(changeOnHand);
  //console.log(arrayOrderReversed);
};

const updateCid = (index, cash) => {
  // * cents array index, to locate current value to update cid
  cid[cid.length - 1 - index][1] =
    Math.round(cid[cid.length - 1 - index][1] * 100 - cash) / 100;
  changeOnHand -= cash;
  arrayOrderReversed[index] -= cash;
  //console.log(cid);
};

const updateDenominationsUi = () => {
  assignDenominations = {};

  const denominations = {
    10000: "ONE HUNDRED",
    2000: "TWENTY",
    1000: "TEN",
    500: "FIVE",
    100: "ONE",
    25: "QUARTER",
    10: "DIME",
    5: "NICKEL",
    1: "PENNY",
  };
  changeDisplayDenominations.forEach((val) => {
    const key = denominations[val];
    if (assignDenominations[key]) {
      // * this checks if it exists, then adds it to the object
      assignDenominations[key] += val;
    } else {
      // * this will run if it does not exists, adding a new value to the object
      assignDenominations[key] = val;
    }
  });
  console.log(assignDenominations);

  let assignDenominationsString = "";
  // * the for loop will loop through the object and log the key into a string
  for (let key in assignDenominations) {
    if (assignDenominations.hasOwnProperty(key)) {
      assignDenominationsString += `${key}: $${
        Math.round((assignDenominations[key] / 100) * 100) / 100
      }<br>`;
    }
  }

  changeDisplay.innerHTML = `Status: OPEN<br>${assignDenominationsString}`;
};

const updateUi = (stat) => {
  drawerDisplay.innerHTML = `<h2>Change in Drawer:</h2>
        <p>Pennies: $${cid[0][1]}</p>
        <p>Nickels: $${cid[1][1]}</p>
        <p>Dimes: $${cid[2][1]}</p>
        <p>Quarters: $${cid[3][1]}</p>
        <p>Ones: $${cid[4][1]}</p>
        <p>Fives: $${cid[5][1]}</p>
        <p>Tens: $${cid[6][1]}</p>
        <p>Twenties: $${cid[7][1]}</p>
        <p>Hundreds: $${cid[8][1]}</p>`;

  if (stat === 0) {
    changeDisplay.style.display = "flex";
    changeDisplay.textContent = "No change due - customer paid with exact cash";
  } else if (stat === 1) {
    changeDisplay.style.display = "flex";
    changeDisplay.innerHTML = "Status: INSUFFICIENT_FUNDS";
  } else if (stat === 2) {
    changeDisplay.style.display = "flex";
    updateDenominationsUi();
  }
};

purchaseBtn.addEventListener("click", () => {
  check();
  userCash.value = "";
});

userCash.addEventListener("keydown", () => {
  if (event.key === "Enter") {
    check();
    userCash.value = "";
  }
}); 

you will need to share also the html

Hi @relzarick

Do not place price and cid in the global scope.

Happy coding

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Cash Register</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <h1>Cash Register Project</h1>
    <div class="input-container">
      <label for="cash">Enter cash from customer:</label>
      <input type="number" id="cash" class="user-input" />
      <button id="purchase-btn">Purchase</button>
    </div>
    <div class="container">
      <div class="price-container">Total: $1.87</div>
      <div id="change-container">
        <h2>Change in Drawer:</h2>
        <p>Pennies: $1.01</p>
        <p>Nickels: $2.05</p>
        <p>Dimes: $3.1</p>
        <p>Quarters: $4.25</p>
        <p>Ones: $90</p>
        <p>Fives: $55</p>
        <p>Tens: $20</p>
        <p>Twenties: $60</p>
        <p>Hundreds: $100</p>
      </div>
    </div>

    <div id="change-due"></div>

    <script src="script.js"></script>
  </body>
</html>

Thanks for replying but why shouldn’t I declare the cid and price var in the global scope?

I use CID in many different functions so wont it be easier to keep it in the global scope?

Each time you run your function, the cid values will revert to the ones in the global scope.

So if you purchase something, the cash register value may not decrease after giving change.

Also, the tests need to use various values for the price.

const updateCid = (index, cash) => {
  // * cents array index, to locate current value to update cid
  cid[cid.length - 1 - index][1] =
    Math.round(cid[cid.length - 1 - index][1] * 100 - cash) / 100;
  changeOnHand -= cash;
  arrayOrderReversed[index] -= cash;
  //console.log(cid);
};

I already have a function here that updates the cid array every time I purchase something

I have checked with console.log that it works

const cal = (change, cashGiven) => {
  let noChange = true;
  changeDisplayDenominations = [];
  // * assume that no changes will be made unless proven otherwise within the iteration

  if (cashGiven === Math.round(price * 100)) {
    while (cashGiven > 0) {
      noChange = true;
      for (let i = 0; i < centsArray.length; i++) {
        if (
          cashGiven >= centsArray[i] &&
          cid[cid.length - 1 - i][1] * 100 >= centsArray[i]
        ) {
          cashGiven -= centsArray[i];
          updateCid(i, centsArray[i]);
          changeDisplayDenominations.push(centsArray[i]);
          noChange = false;
          break;
        }
      }
      if (noChange) {
        break;
      }
    }
  }

While checking my code again I noticed that in the parameter of my IF block, I had set it to check for the value 187 specifically instead of the price. I have updated it but I am still unable to pass

they must be in the global scope… no other variable should be tho (not counting those that are used to refer to html elements)

let changeDisplayDenominations = [];
let assignDenominations = {};

let changeOnHand =
  Number(cid.reduce((acc, curVal) => acc + curVal[1], 0).toFixed(2)) * 100;

let arrayOrderReversed = cid.map((arr) => Math.round(arr[1] * 100)).reverse();
const centsArray = [10000, 2000, 1000, 500, 100, 25, 10, 5, 1];

I have a few var in the global scope used for calculations in different functions, is it okay to leave them there?

Im pretty sure my code works exactly how the tests wants it to, I just cant seem to pass

No, it’s not okay to leave them there. Global variables keep their value between each time the function is run, so the second time they do not start empty and the result is skewed.

Your code contains global variables that are changed each time the function is run. This means that after each function call completes, subsequent function calls start with the previous value. To fix this, make sure your function doesn’t change any global variables, and declare/assign variables within the function if they need to be changed.

Example:

var myGlobal = [1];
function returnGlobal(arg) {
  myGlobal.push(arg);
  return myGlobal;
} // unreliable - array gets longer each time the function is run

function returnLocal(arg) {
  var myLocal = [1];
  myLocal.push(arg);
  return myLocal;
} // reliable - always returns an array of length 2

What do you mean by my result will be skewed?
For the other global var I keep them there because I want it to be updated so that they can be used in other functions. Such as updating my innerhtml

It’s like not clearing the memory of a calculator when doing the next calculation.

1+1 = 2
1+1 = 4

results are skewed

Thanks for the reply. I have another question, why should the price and cid be declared globally while my other var such as arrayOrderReversed have to local?
What is the correct practice for declaring such var?
Should I never modify a global var? Opting to make a copy of it in a local scope instead?

In my code, I actually want it to keep the memory of past modifications so I can edit the innerHTML for the display. Is that a bad practice?

Every time the function is called, it needs to calculate based on the arguments given.

Put your variables where they need to be so that when your function is called twice, it gives the correct results.

Test as needed by calling your function twice and examine the results.

@pkdvalis why you too? cid and price MUST be GLOBAL, see tests 2 and 3

@relzarick the two variables cid and price are needed for the tests, the tests would not be able to access and change these if they would be local somewhere, so you have them global. Your function should also not change them.

there are situations in which changing a global var is warranted. This is not one of them.
In your case, if you have global variables that aides local calculations, you risk that a previous calculation pollutes the output of a future one, like in this example:

the value of returnLocal depends only on arg, so for a certain input you always have the same output.
returnGlobal depends on a global variable that is changed so the output is unreliable if you want it to depends only on the argument of the function.

returnLocal(1) returns always [1, 1]
returnGlobal(1), returns first [1, 1] but if you call it again then it returns [1, 1, 1]

your app should base its calculations only on cid price and the cash value from the input element, the same three values should always give the same output.
If you add in global variables that are changed each time the function is called, the function output depends on more things now.

Sorry, I didn’t read this thread thoroughly enough :sweat:

I though it was a pure function.
Wait, then how is this a cash register?
It works out the change, then what happens to the cash from the customer?