Cash Register : Can't figure out one case

Hi there !

I’ve tried multiple solutions, thinking about a precision problem but I’m not sure anymore. I pass all tests except one. I would like a little hint on my mistake.

Oh and my code construction is pretty messy, I’m sorry for that but if you have any tips to improve, feebacks are welcome !

function checkCashRegister(price, cash, cid) {
  var change = cash - price;
  let ticket = {status: "", change: []};
  const values = [1, 5, 10, 25, 100, 500, 1000, 2000, 10000];

  function howMuchDrawer(array) {
    return array.reduce((acc, current) => {
      return acc + current[1];
    }, 0);
  }

  // Basics solutions, no enough money or exact change
  if (change > howMuchDrawer(cid)) {
    ticket.status = "INSUFFICIENT_FUNDS";
  } else if (change == howMuchDrawer(cid)) {
    ticket.status = "CLOSED";
    ticket.change = cid;

  // Actual problem. I've tried to *100 in case of a precision mistake, but the behavior didn't change. 
  } else {
    change = Math.round((cash - price) * 100);
    cid.forEach(el => el[1] = Math.round(el[1] * 100));
    for (var i = cid.length - 1; i >= 0; i--) {
      var dueChange = 0;
      // Here is the problem, my algo think that I can return money for the 5th test.
      while (cid[i][1] > 0 && change >= values[i]) {
        change -= values[i];
        cid[i][1] -= values[i];
        dueChange += values[i];
      }
      if (dueChange) {
        ticket.status = "OPEN";
        ticket.change.push([cid[i][0], dueChange]);
      }
    }
    ticket.change.forEach(el => el[1] = (el[1] / 100));
  }
  return ticket;

}

Challenge link : https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/javascript-algorithms-and-data-structures-projects/cash-register

It looks like you are tracking a decimal value of dollars. You will always have problems with precision when using floating point numbers because its not possible for your computer to accurately store decimal numbers without roundoff.

I recommend using an integer number of cents instead.

I tried another approach, by directly putting the cents in integers.
My problem is that I can’t seem to tell my program “If you don’t have the perfect amount of change to give change, then you return {status:” INSUFFICIENT_FUNDS ", change: }

It returns the only value it is able to return (0.01) before stopping. I don’t know how to add the condition “If dueChange == initialChange” stay OPEN, else return INSUFFICIENT_FUNDS

I am not sure that I am clear, or even that I understand my mistake.

function checkCashRegister(price, cash, cid) {
  var change = Math.round((cash - price) * 100);
  let ticket = {status: "", change: []};
  const values = [1, 5, 10, 25, 100, 500, 1000, 2000, 10000];
  cid.forEach(el => el[1] = Math.round(el[1] * 100));

  function howMuchDrawer(array) {
    return array.reduce((acc, current) => {
      return acc + current[1];
    }, 0);
  }

  // Basics solutions, no enough money or exact change
  if (change > howMuchDrawer(cid)) {
    ticket.status = "INSUFFICIENT_FUNDS";
  } else if (change == howMuchDrawer(cid)) {
    ticket.status = "CLOSED";
    ticket.change = cid;

  // Actual problem. I've tried to *100 in case of a precision mistake, but the behavior didn't change. 
  } else {
    for (var i = cid.length - 1; i >= 0; i--) {
      var dueChange = 0;
      // Here is the problem, my algo think that I can return money for the 5th test.
      while (cid[i][1] > 0 && change >= values[i]) {
        change -= values[i];
        cid[i][1] -= values[i];
        dueChange += values[i];
      }
      if (dueChange) {
        ticket.status = "OPEN";
        ticket.change.push([cid[i][0], dueChange]);
      }
    } 
  }
  ticket.change.forEach(el => el[1] = (el[1] / 100));

  return ticket;
}

checkCashRegister(19.5, 20, [["PENNY", 0.01], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 1], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]) should return {status: "INSUFFICIENT_FUNDS", change: [ ]}

And it return :
{ status: 'OPEN', change: [ [ 'PENNY', 0.01 ] ] }

Okay, I believe I see the error in the logic here.

So it iterates (backwards) through your cid array. It sees there are 0 “ONE HUNDRED” and moves to “TWENTY”, sees that there are 0 “TWENTY” and moves to “TEN”, and so on. It hits “ONE”, sees that there is 100 in “ONE”, but change is only “50” so it moves on.

Then it hits “PENNY” and sees there is 1. 1 is greater than 0, and change is 50 so the while loop runs. It takes the 1 out of 50 and out of “PENNY”, pushes to dueChange, and the for loop ends.

Now your ticket.status is OPEN and you have the penny in your change, so this gets returned. You want to check if dueChange == initialChange but I think that is the incorrect approach. Instead, after you’ve gone through your for loop and calculated your change, check if dueChange is 0. If it IS, you have enough cash to return the customer’s change. But if it is NOT, then you have INSUFFICIENT_FUNDS to return the change. :slight_smile:

Thanks for your help !
I’ve tried a simple console.log(dueChange) right after the for loop. Now dueChange is then worth 1. If I try with this test :

checkCashRegister(3.26, 100, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]]) should return {status: "OPEN", change: [["TWENTY", 60], ["TEN", 20], ["FIVE", 15], ["ONE", 1], ["QUARTER", 0.5], ["DIME", 0.2], ["PENNY", 0.04]]} .

Then, console.log(dueChange) return 4 even if I have enough cash. :worried:

If you are checking the value of dueChange after the for loop, it makes sense that you would see 4. dueChange holds the value of the change being returned in the current denomination - your loop ends with pennies, which if you look at the expected return in that test you see 4 pennies. :slight_smile:

I made an error in my previous post, I think - instead of checking dueChange == 0, I meant to check change == 0. :slight_smile:

1 Like

Alright, you’re completely right ! Thank you again for your help and for your precise explanations !
I’ll have to rework this challenge 'til being able to found a solution by myself. :upside_down_face:

1 Like

Glad I was able to help!

It can be fun to rework your solutions - I just rebuilt this one myself yesterday.