Problem on Cash Register challenge (now w/ solution)

Hi there,

I’m working on the last challenge of the JavaScript Algorithms and Data Structures Course, which is to build a Cash Register. Obviously, I am not finished yet, but I am stuck on one problem (before continuing) - to understand it I’ll show you my code, first.

function checkCashRegister(price, cash, cid) {
  let change = cash - price;
  console.log("change: " + change);

  const currencyVal = {
    "ONE HUNDRED": 100,
    "TWENTY": 20,
    "TEN": 10,
    "FIVE": 5,
    "ONE": 1,
    "QUARTER": 0.25,
    "DIME": 0.1,
    "NICKEL": 0.05,
    "PENNY": 0.01
  }

  const cidSum = cid.reduce((acc, val) => {
    if(!isNaN(acc + val[1])) {
      return acc + val[1];
    }
  }, 0);

  const revCid = cid.reverse();
  let changeObj = {}

  function calculateChange() {
    for(let unit in currencyVal) { // loop through each Currency Unit
      revCid.forEach(amount => {  // loop through each amount
        if(amount[1] >= currencyVal[unit] && amount[0] === unit) { // check what I have in cash-in-drawer
          while(change - currencyVal[unit] >= 0 && amount[1]) {
            // calculate the change and convert it into an object
            console.log(unit, currencyVal[unit]);
            if(Object.keys(changeObj).length === 0 && changeObj.constructor === Object) {
              changeObj[unit] = currencyVal[unit];
            } else {
              if(changeObj.hasOwnProperty(unit)) {
                changeObj[unit] += currencyVal[unit];
              }
              if(!changeObj.hasOwnProperty(unit)) {
                changeObj[unit] = currencyVal[unit];
              }
            }
            
            change -= currencyVal[unit];
            amount[1] -= currencyVal[unit];
          }
        }
      });
    }

    let changeArr = Object.entries(changeObj); // convert changeObj into an Array
    return changeArr;
  }

  if(change < 0 || change > cidSum) {
    return {status: "INSUFFICIENT_FUNDS", change: []}
  } else {
    return {status: "OPEN", change: calculateChange()};
  }
}

console.log(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]]));

So, if you run this code, you’ll notice that there’s just 0.01 PENNY missing. I want to pay my imaginary customers fully, so I need help :upside_down_face: Where’s the issue? This is the first time, I’m asking on this forum, please forgive me, if I missed something on this post. I appreciate any solution!

This is a very common problem with this challenge. If you aren’t very familiar with how JS stores floating point numbers then it is easy to make this mistake. Open up your browser’s dev tools console and at the prompt type:

0.1 + 0.2

and see the answer that you get. Here’s a very in depth explanation of what is going on. Bottom line, if you need pinpoint accuracy with floating point numbers then you can’t just use them as is, you’ll need to work around this limitation. There are libraries out there that can help. Sometimes you can get away with rounding. For this challenge, I would recommend that instead of storing the values in dollars, you pick another unit that doesn’t require using decimal points. I’ll let you figure out what that should be :slight_smile:

1 Like

Thank you Bruce! You answered my question and even still letting me figuring out the solution by myself. Now I deeply understand. My solution now passes all tests. If there’s anything else to make this solution like “perfect”, please let me know :slightly_smiling_face: For me a good code has to be understood by little ants.

function checkCashRegister(price, cash, cid) {
  const cidCopy = JSON.parse(JSON.stringify(cid)); // JSON limitation might be slow, but for some reason even the faster solutions, like .slice(0) and the other ones, might not work without affecting the original array
  const revCid = cidCopy.reverse();

  const currencyVal = {
    "ONE HUNDRED": 100,
    "TWENTY": 20,
    "TEN": 10,
    "FIVE": 5,
    "ONE": 1,
    "QUARTER": 0.25,
    "DIME": 0.1,
    "NICKEL": 0.05,
    "PENNY": 0.01
  }

  // sum up what we have in cash-in-drawer
  const cidSum = cid.reduce((acc, val) => {
    if(!isNaN(acc + val[1])) {
      return acc + val[1];
    }
  }, 0);

  // convert units in order to work aroung floating point number limitations
  function convertDollarToCent(val) {
    if(!Array.isArray(val) && typeof val === "object") {
      const currencyValCopy = {...val};

      for(let dollar in currencyValCopy) {
        currencyValCopy[dollar] *= 100;
      }
      return currencyValCopy;
    }
    if(Array.isArray(val)) {
      const currencyValCopy = [...val];

      currencyValCopy.forEach(dollar => {
        return dollar[1] *= 100;
      });
      return currencyValCopy;
    }
    else {
      return val * 100;
    }
  }
  const centPrice = convertDollarToCent(price);
  const centCash = convertDollarToCent(cash);
  const centUnit = convertDollarToCent(currencyVal);
  const centRevCid = convertDollarToCent(revCid);
  const centCidSum = convertDollarToCent(cidSum);
  let change = centCash - centPrice;

  let changeObj = {}

  function calculateChange() {
    for(let unit in centUnit) { // loop through each Currency Unit
      centRevCid.forEach(amount => {  // loop through each amount
        if(amount[1] >= centUnit[unit] && amount[0] === unit) { // check what I have in cash-in-drawer
          while(change - centUnit[unit] >= 0 && amount[1]) {
            // calculate the change and convert it into an object
            if(Object.keys(changeObj).length === 0 && changeObj.constructor === Object) {
              changeObj[unit] = centUnit[unit];
            } else {
              if(changeObj.hasOwnProperty(unit)) {
                changeObj[unit] += centUnit[unit];
              }
              if(!changeObj.hasOwnProperty(unit)) {
                changeObj[unit] = centUnit[unit];
              }
            }

            change -= centUnit[unit];
            amount[1] -= centUnit[unit];
          }
        }
      });
    }

    let changeArr = Object.entries(changeObj); // convert changeObj into an Array
    // convert the change-unit back from Cent into Dollar
    changeArr.forEach(cent => {
      return cent[1] /= 100;
    });

    return changeArr;
  }

  let sumChangeArr = calculateChange()
    .reduce((acc, val) => {
      if(!isNaN(acc + val[1])) {
        return acc + val[1] * 100;
      }
    }, 0);

  if(change < 0 || centCidSum - change < 0 || change > sumChangeArr) {
    return {status: "INSUFFICIENT_FUNDS", change: []};
  } else if(centCidSum === sumChangeArr) {
    return {status: "CLOSED", change: cid};
  } else {
    return {status: "OPEN", change: calculateChange()};
  }
}

console.log(checkCashRegister(19.5, 20, [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]));

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.