Build a cash register bug?

  1. When
price

is less than the value in the

#cash

element, total cash in drawer

cid

is equal to change due, and the

#purchase-btn

element is clicked, the value in the

#change-due

element should be

"Status: CLOSED"

with change due in coins and bills sorted in highest to lowest order.

My code fails test 19. But when I press multiple times, the code passes.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="Arlen Nigtshade">
    <meta name="description" content="Cash Register">
    <title>Cash Register</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <input id="cash">
    <p id="change-due">output</p>
    <button id="purchase-btn">Purchase</button>
    <script src="script.js"></script>
</body>
</html>
let price = 19.5;
let cid = [
  ["PENNY", 0.5],
  ["NICKEL", 0],
  ["DIME", 0],
  ["QUARTER", 0],
  ["ONE", 0],
  ["FIVE", 0],
  ["TEN", 0],
  ["TWENTY", 0],
  ["ONE HUNDRED", 0],
];
const input = document.getElementById("cash");
const output = document.getElementById("change-due");
const button = document.getElementById("purchase-btn");
let status = "OPEN";
let sufficientFunds = true;

const stringToMoney = (string) => {
  const reference = {
    PENNY: 0.01,
    NICKEL: 0.05,
    DIME: 0.1,
    QUARTER: 0.25,
    ONE: 1,
    FIVE: 5,
    TEN: 10,
    TWENTY: 20,
    "ONE HUNDRED": 100,
  };
  return reference[string];
};

function roundTohundredth(num) {
  return Math.round(num * 100) / 100;
}

function checkStatus() {
  let change = parseFloat(input.value) - price;
  if (change === cid.reduce((acc, x) => acc + x[1], 0)) {
    return "CLOSED";
  } else {
    return "OPEN";
  }
}

function calculateChange(cash) {
  let change = cash - price;
  const changeUnitsObj = {};

  cid.sort((a, b) => stringToMoney(b[0]) - stringToMoney(a[0]));
  cid.forEach((pair) => {
    while (change >= stringToMoney(pair[0]) && pair[1] > 0) {
      pair[1] -= stringToMoney(pair[0]);
      change -= stringToMoney(pair[0]);
      change = roundTohundredth(change);
      if (changeUnitsObj[pair[0]]) {
        changeUnitsObj[pair[0]] += stringToMoney(pair[0]);
      } else {
        changeUnitsObj[pair[0]] = stringToMoney(pair[0]);
      }
      changeUnitsObj[pair[0]] = roundTohundredth(changeUnitsObj[pair[0]]);
    }
  });
  if (change > 0) {
    return null;
  }
  return changeUnitsObj;
}

function createMsg() {
  let message = `Status: ${checkStatus()}`;
  const changeObj = calculateChange(input.value);
  if (parseFloat(input.value) === price) {
    return "No change due - customer paid with exact cash";
  }
  if (!changeObj) {
    return "Status: INSUFFICIENT_FUNDS";
  }
  for (let key in changeObj) {
    message += ` ${key}: $${changeObj[key]}`;
  }

  return message;
}

function purchase() {
  if (parseFloat(input.value) < price) {
    alert("Customer does not have enough money to purchase the item");
  }
  output.textContent = createMsg();
  input.value = "";
}

// input.addEventListener("change", purchase);
button.addEventListener("click", purchase);

The test also doesn’t like addEventListener “change” on input so I’ve commented it out. It’s not really a big deal though.

You probably are running into floating point arithmetic issues. Look at any area where you are doing arithmetic with floats and make sure you are using recommended methods to handle the result.

function roundTohundredth(num) {
  return Math.round(num * 100) / 100;
}

I did run to an issue earlier so I made the function above. The other solution the I’ve found was use the toFixed() method but it returns as as string so I didn’t use it.

I would stick to using cents personally

like if 1 dollar is now 100 cents and then just convert it after the calculation?

1 Like

Exactly like that. Then you are working with cents for all the addition and subtraction.

1 Like

you have to account for rounding errors whenever you do any arithmetic, for eg here:

or here:

please read this article:

1 Like
let price = 3.26;
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 input = document.getElementById("cash");
const output = document.getElementById("change-due");
const button = document.getElementById("purchase-btn");

const stringToCents = (string) => {
  const reference = {
    PENNY: 1,
    NICKEL: 5,
    DIME: 10,
    QUARTER: 25,
    ONE: 100,
    FIVE: 500,
    TEN: 1000,
    TWENTY: 2000,
    "ONE HUNDRED": 10000,
  };
  return reference[string];
};

function checkStatus() {
  let change = parseFloat(input.value) - price;
  if (change === cid.reduce((acc, x) => acc + x[1], 0)) {
    return "CLOSED";
  } else {
    return "OPEN";
  }
}

function calculateChange(cash) {
  let change = (cash - price) * 100;
  let changeUnitsObj = {};

  cid.sort((a, b) => stringToCents(b[0]) - stringToCents(a[0]));

  cid = cid.map((arr) => [arr[0], arr[1] * 100]);
  cid.forEach((pair) => {
    while (change >= stringToCents(pair[0]) && pair[1] > 0) {
      pair[1] -= stringToCents(pair[0]);
      change -= stringToCents(pair[0]);
      if (changeUnitsObj[pair[0]]) {
        changeUnitsObj[pair[0]] += stringToCents(pair[0]);
      } else {
        changeUnitsObj[pair[0]] = stringToCents(pair[0]);
      }
    }
  });
  if (change > 0) {
    return null;
  }

  for (let key in changeUnitsObj) {
    changeUnitsObj[key] = changeUnitsObj[key] / 100;
  }

  return changeUnitsObj;
}

function createMsg() {
  let message = `Status: ${checkStatus()}`;
  const changeObj = calculateChange(parseFloat(input.value));
  if (parseFloat(input.value) === price) {
    return "No change due - customer paid with exact cash";
  }
  if (!changeObj) {
    return "Status: INSUFFICIENT_FUNDS";
  }
  for (let key in changeObj) {
    message += ` ${key}: $${changeObj[key]}`;
  }

  return message;
}

function purchase() {
  if (parseFloat(input.value) < price) {
    alert("Customer does not have enough money to purchase the item");
  }
  output.textContent = createMsg();
  input.value = "";
}

// input.addEventListener("change", purchase);
button.addEventListener("click", purchase);

I did as suggested. Thanks for the suggestion by the way. It made things a lot simpler. But it still fails test 19, and passes when run code is click multiple times. At this point, I’m confused whether I actually passed or not.

Are you sorting smallest to biggest or biggest to smallest? You want to return biggest to smallest here.

I’m sorting descending.
Screenshot 2024-10-01 190718
is my sorting causing an issue?

That nickel looks suspicious, doesn’t it?

2 Likes

Screenshot 2024-10-02 093553

  cid = cid.map((arr) => [arr[0], Math.round(arr[1] * 100)]);

I tested multiple times before making this changes. Sometimes the nickel becomes the long decimal. Sometimes it doesn’t. After I made the changes it stays like that consistently. It still takes multiple click to pass.

this is that floating point arithmetic issue. You have to check your code for all the arithmentic operations and fix them all.

1 Like

This is definitely better.

I’m not sure what else is up but I do recall seeing some sort of an issue on this challenge recently.

1 Like

I’ll try making a duplicate of the project using the toFixed() method this time. Thanks for helping me with this and from my earlier projects as well. You too @hbar1st.

1 Like

I don’t think you need to go the toFixed() route. That wouldn’t fix a test suite issue and the rest of your code looks correct to me.

I see. Should I move on then?

The only other thing you could possibly change that I see is maybe

let change = Math.round((cash - price) * 100));

to make sure everything is an integer.

Otherwise if you have a passing run I’d call it good.

1 Like