I created a "Cash Register" function for Freecodecamp but for some reason the last two tests aren't passing?

I created this function but the last two tests

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: []}.
checkCashRegister(19.5, 20, [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]) should return {status: "CLOSED", change: [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]}.

don't pass. What is wrong in my code?

The task is:

Design a cash register drawer function checkCashRegister() that accepts purchase price as the first argument (price), payment as the second argument (cash), and cash-in-drawer (cid) as the third argument.

cid is a 2D array listing available currency.

The checkCashRegister() function should always return an object with a status key and a change key.

Return {status: "INSUFFICIENT_FUNDS", change: []} if cash-in-drawer is less than the change due, or if you cannot return the exact change.

Return {status: "CLOSED", change: [...]} with cash-in-drawer as the value for the key change if it is equal to the change due.

Otherwise, return {status: "OPEN", change: [...]}, with the change due in coins and bills, sorted in highest to lowest order, as the value of the change key.

My code:

const INSUFFICIENT_FUNDS = {
  status: 'INSUFFICIENT_FUNDS',
  change: []
};
const CLOSED_CID = {
  status: 'CLOSED',
  change: []
};
const POSITION_CID = [
  ['ONE HUNDRED', 100],
  ['TWENTY', 20],
  ['TEN', 10],
  ['FIVE', 5],
  ['ONE', 1],
  ['QUARTER', 0.25],
  ['DIME', 0.1],
  ['NICKEL', 0.05],
  ['PENNY', 0.01]
];
const add = (a, b) => a + b[1];
const checkCashRegister = (price, cash, cid) => {
  let finalChange = [];
  let changeDue = cash - price;
  const cidSum = cid.reduce(add, 0).toFixed(2);
  /*
  If there is exactly enough money to provide change, the
  status key is “CLOSED”, and the change key is our
  cash-in-drawer(cid).
  */
  if (cidSum == changeDue) {
    return {
      CLOSED_CID,
      change: cid
    };
  }
  /*
  If there is not enough money to provide change, the status key
  is “INSUFFICIENT_FUNDS” and the change key is an empty array.
  */
  if (cidSum < changeDue) {
    return INSUFFICIENT_FUNDS;
  }
  /*
  If there is enough money to provide change with money still
  left in the drawer, the change is then provided by going down
  a list of currency units from high to low, pushing them to
  the change array, and subtracting them from both the cash
  owed and the cash-in-drawer(cid).
  */
  let cidReverse = cid.reverse();
  POSITION_CID.map((posCid, index) => {
    const billValue = posCid[1];
    while (changeDue >= billValue && cidReverse[index][1] >= billValue) {
      changeDue -= billValue; // minus change due from bill value
      changeDue = changeDue.toFixed(2); // fix rounding errors
      const hasBillValueAlready = finalChange.filter(p => p[0] === cidReverse[index][0]);
      if (hasBillValueAlready.length > 0) {
        finalChange = finalChange.map(k => (k[0] === posCid[0] && [k[0], (k[1] += billValue)]) || [k[0], k[1]]);
      } else {
        finalChange.push([cidReverse[index][0], billValue]);
      }
      cidReverse[index][1] -= billValue; // minus bill value from cash-in-drawer
    }
  });
  if (changeDue !== 0) {
    return {
      status: 'OPEN',
      change: finalChange
    };
  } else {
    return INSUFFICIENT_FUNDS;
  }
};
const price = 19.5;
const cash = 200;
const cid = [
  ['PENNY', 1.01],
  ['NICKEL', 2.05],
  ['DIME', 3.1],
  ['QUARTER', 4.25],
  ['ONE', 90],
  ['FIVE', 55],
  ['TEN', 20],
  ['TWENTY', 60],
  ['ONE HUNDRED', 100]
];
// checkCashRegister(price, cash, cid);

console.log(
checkCashRegister(19.5, 20, [
  ["PENNY", 1.01],
  ["NICKEL", 2.05],
  ["DIME", 3.1],
  ["QUARTER", 4.25],
  ["ONE", 90],
  ["FIVE", 55],
  ["TEN", 20],
  ["TWENTY", 60],
  ["ONE HUNDRED", 100]
]),
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: []}`,
checkCashRegister(19.5, 20, [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]), "should return", `{status: "CLOSED", change: [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]}`)

Two issues to fix:

  • When the change due is exactly what is in the cash register, the code returns an object that does not have the right structure:

    return {
      CLOSED_CID,
      change: cid
    };
    

    This will create a property CLOSED_CID which has as value an object with a nested status and change property... and you add yet another change property at the parent level. Instead, you should just forget about the predefined CLOSED_CID object, and do this:

    return {
      status: "CLOSED",
      change: cid
    };
    
  • The decision whether to return the status OPEN or INSUFFICIENT_FUNDS should be on the opposite condition. It is when you have attributed all of the change amount to bills that you should return OPEN. So change this:

    if (changeDue !== 0) {
      return {
        status: 'OPEN',
        change: finalChange
      };
    } else {
      return INSUFFICIENT_FUNDS;
    }
    

    to:

    if (changeDue == 0) { // All change could be translated to available bills/coins
      return {
        status: 'OPEN',
        change: finalChange
      };
    } else { // There is change remaining that cannot be covered by available bills/coins
      return INSUFFICIENT_FUNDS;
    }
    

As a remark: the toFixed method returns a string. It would be better to explicitly convert the result back to number data type. There is currently no problem with it in your code, as that conversion happens implicitly when you do -= and ==, but for code maintenance reasons you'd better not rely on that. So add a unary + whenever you call toFixed:

const cidSum = +cid.reduce(add, 0).toFixed(2);
// ...
changeDue = +changeDue.toFixed(2);

I would however suggest to do all calculations in cents, so all calculations are done with integer values, which do not suffer from the imprecision you otherwise need to cope with.

I assume you already found solutions on the web, and there are many Q&A on Stack Overflow about this same challenge.