Cash Register Confusion: I'm a penny short?!

I have come up with a solution (albeit longwinded) but it is ALMOST correct. I am one cent short of the answer for the test argument I’m checking. It should be [“PENNY”, 0.04] but in my finalArr array I get [“PENNY”, 0.03]

I’m assuming there is a rounding error occurring somewhere in my code. Am I on the right track?

I have included comments before each step/section to help explain my thought process.
Any constructive feedback is welcome!


function checkCashRegister(price, cash, cid) {
 var change = cash - price;
 console.log('change =', change)

// sum all cash in drawer
 var cidSum = 0;
 for (var i = 0; i < cid.length; i++) {
   cidSum += cid[i][1];
 }
// WHY DOES IT PRODUCE A REPEATING DECIMAL???
// console.log(cidSum)
// round to the nearest hundredth
 cidSum = Math.round(100*cidSum)/100;
// console.log('cidSum =', cidSum)


// array of unit values
 var unit = [0.01, 0.05, 0.1, 0.25, 1, 5, 10, 20, 100];

// array of how many units each are available in drawer
 var numOfUnit = [];
 for (var i = 0; i < cid.length; i++) {
   numOfUnit.push(Math.round(cid[i][1]/unit[i]))
 }
// console.log(numOfUnit)

// start at hundred unit + loop backwards
// substract unit value from change total
// decrement the number of currency units available
// push values into changeDue array
 var changeDue = [];
 for (var i = unit.length - 1; i >= 0; i--) {
   while (change > unit[i] && numOfUnit[i] > 0) {
     change -= unit[i];
     //console.log(change)
     numOfUnit[i]--;
     //console.log(numOfUnit)
     changeDue.push(unit[i]);
     }
   }
// console.log('changeDue:', changeDue)
// console.log(change)

// create empty 2D array to add currency values
 var returnArr = 
 [["ONE HUNDRED", 0],
 ["TWENTY", 0],
 ["TEN", 0],
 ["FIVE", 0],
 ["ONE", 0],
 ["QUARTER", 0],
 ["DIME", 0],
 ["NICKEL", 0],
 ["PENNY", 0]];

// add values to empty array for each unit of currency
 for (var i = 0; i < changeDue.length; i++) {
   if (changeDue[i] == 100) {
   returnArr[0][1] += 100;
 } else if (changeDue[i] == 20) {
   returnArr[1][1] += 20;
 } else if (changeDue[i] == 10) {
   returnArr[2][1] += 10;
 } else if (changeDue[i] == 5) {
   returnArr[3][1] += 5;
 } else if (changeDue[i] == 1) {
   returnArr[4][1] += 1;
 } else if (changeDue[i] == 0.25) {
   returnArr[5][1] += 0.25;
 } else if (changeDue[i] == 0.1) {
   returnArr[6][1] += 0.1;
 } else if (changeDue[i] == 0.05) {
   returnArr[7][1] += 0.05;
 } else if (changeDue[i] == 0.01) {
   returnArr[8][1] += 0.01;
 }
}

// include unit of currency only if value > 0
 var finalArr = []
 for (var i = 0; i < unit.length; i++) {
   if (returnArr[i][1] > 0){
   finalArr.push(returnArr[i])
 }
}
console.log(finalArr)

// sum values from finalArr to get exactChange total
 var exactChange = 0;
 for (var i = 0; i < finalArr.length; i++) {
   exactChange += finalArr[i][1];
 }
console.log('exactChange =', exactChange)

// 1. Is there enough change/can u make exact change?
 if (cidSum < change || change != exactChange) {
   return {status: "INSUFFICIENT_FUNDS", change: []};
 }

// 2. Is cash in drawer sum == change due?
 if (cidSum == change) {
   return {status: "CLOSED", change: [cid]};
 }

// 3. Return array with change in decreasing value
 return {status: "OPEN", change: finalArr};
}

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]]));
// should return...
// {status: "OPEN", change: [["TWENTY", 60], ["TEN", 20], ["FIVE", 15], ["ONE", 1], ["QUARTER", 0.5], ["DIME", 0.2], ["PENNY", 0.04]]}
   **Your browser information:**

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

Challenge: Cash Register

Link to the challenge:

I have not read all of your code yet, but I know some general things that can occur with this specific challenge is that if the values become floats they can go from being say 2.08 to being 2.0799999999999999 (infinite nines) now obviously a true infinitely repeating number cannot be made, and this will skew your values.

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]]));
//{ status: 'INSUFFICIENT_FUNDS', change: [] }
//should have been
//{status: "CLOSED", change: [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]}
//If you take a look at how many pennies you have left however
//[ 'PENNY', 0.49000000000000027 ]

You need to force the values to round correctly, this will help: Number.prototype.toFixed() - JavaScript | MDN
toFixed returns a string so you will need to make sure to change it back to a number

1 Like

Yes and no. As you have noticed after adding all cash in drawer cidSum ends up almost what it should. Operations on float numbers can be imprecise, this is caused by the way they are internally represented (if you find this interesting I’d suggest search for this topic for more specifics). I’d suggest to try to figure out way that would make all (or almost all) operations to be performed on integers.

1 Like

So I tried using toFixed() and parseFloat().

// sum all cash in drawer
  var cidSum = 0;
  for (var i = 0; i < cid.length; i++) {
    cidSum += Number.parseFloat(cid[i][1]).toFixed(2)
    console.log(cidSum)
  }

But it just concats the floats as strings. Shouldn’t parseFloat convert the fixed decimal string back to a number??
01.01
01.012.05
01.012.053.10
01.012.053.104.25
01.012.053.104.2590.00
01.012.053.104.2590.0055.00
01.012.053.104.2590.0055.0020.00
01.012.053.104.2590.0055.0020.0060.00
01.012.053.104.2590.0055.0020.0060.00100.00

you are calling toFixed on the number created by parseFloat so parseFloat is happening first then toFixed happens

console.log(Number.parseFloat(1.222).toFixed(2)) //'1.22'

This would work Number(cid[i][1].toFixed(2))

1 Like

I just tried putting parentheses around (cid[i][1]).toFixed(2)
So that fixes the concatenation issue, but why arent all the values forced to round correctly?

// sum all cash in drawer
  var cidSum = 0;
  for (var i = 0; i < cid.length; i++) {
    cidSum += Number.parseFloat((cid[i][1]).toFixed(2))
    console.log(cidSum)
1.01
3.0599999999999996
6.16
10.41
100.41
155.41
175.41
235.41
335.40999999999997

Okay I just did:

var cidSum = 0;
  for (var i = 0; i < cid.length; i++) {
    cidSum += Number(cid[i][1].toFixed(2))
    console.log(cidSum)
  }

Output:
1.01
3.0599999999999996
6.16
10.41
100.41
155.41
175.41
235.41
335.40999999999997

Not sure what is happening. :dizzy_face:

Make sure you are using Number(cid[i][1].toFixed(2)) when are doing something can cause floats, such as division

1 Like

Remember += operator there is an equivalent of writing it:

cidSum = cidSum + Number(cid[i][1].toFixed(2))

This will still have the same precision issues.

1 Like

Okay I will make sure to do that, but why is it still producing a repeating decimal when I am using Number(cid[i][1].toFixed(2))?

var cidSum = 0;
  for (var i = 0; i < cid.length; i++) {
    cidSum += Number(cid[i][1].toFixed(2))
    console.log(cidSum)
  }

Output:
1.01
3.0599999999999996
6.16
10.41
100.41
155.41
175.41
235.41
335.40999999999997

Why is this not producing a fixed rounded value for each execution of the loop? I’m just finding a sum, not performing division, correct?

can you put down all of your current code

1 Like

Okay its pretty much the same as before. It’s kinda long.

function checkCashRegister(price, cash, cid) {
  var change = cash - price;
  console.log('change =', change)

// sum all cash in drawer
  var cidSum = 0;
  for (var i = 0; i < cid.length; i++) {
    cidSum += Number(cid[i][1].toFixed(2))
    //console.log(cidSum)
  }
// WHY DOES IT PRODUCE A REPEATING DECIMAL???
//console.log(cidSum)
// round to the nearest hundredth
  cidSum =  Math.round(100*cidSum)/100;
 //console.log('cidSum =', cidSum)


// array of unit values
  var unit = [0.01, 0.05, 0.1, 0.25, 1, 5, 10, 20, 100];

// array of how many units each are available in drawer
  var numOfUnit = [];
  for (var i = 0; i < cid.length; i++) {
    numOfUnit.push(Math.round(cid[i][1]/unit[i]))
  }
 console.log(numOfUnit)

// start at hundred unit + loop backwards
// substract unit value from change total
// decrement the number of currency units available
// push values into changeDue array
  var changeDue = [];
  for (var i = unit.length - 1; i >= 0; i--) {
    while (change > unit[i] && numOfUnit[i] > 0) {
      change -= unit[i];
      //console.log(change)
      numOfUnit[i]--;
      //console.log(numOfUnit)
      changeDue.push(unit[i]);
      }
    }
// console.log('changeDue:', changeDue)
// console.log(change)

// create empty 2D array to add currency values
  var returnArr = 
  [["ONE HUNDRED", 0],
  ["TWENTY", 0],
  ["TEN", 0],
  ["FIVE", 0],
  ["ONE", 0],
  ["QUARTER", 0],
  ["DIME", 0],
  ["NICKEL", 0],
  ["PENNY", 0]];

// add values to empty array for each unit of currency
  for (var i = 0; i < changeDue.length; i++) {
    if (changeDue[i] == 100) {
    returnArr[0][1] += 100;
  } else if (changeDue[i] == 20) {
    returnArr[1][1] += 20;
  } else if (changeDue[i] == 10) {
    returnArr[2][1] += 10;
  } else if (changeDue[i] == 5) {
    returnArr[3][1] += 5;
  } else if (changeDue[i] == 1) {
    returnArr[4][1] += 1;
  } else if (changeDue[i] == 0.25) {
    returnArr[5][1] += 0.25;
  } else if (changeDue[i] == 0.1) {
    returnArr[6][1] += 0.1;
  } else if (changeDue[i] == 0.05) {
    returnArr[7][1] += 0.05;
  } else if (changeDue[i] == 0.01) {
    returnArr[8][1] += 0.01;
  }
}

// include unit of currency only if value > 0
  var finalArr = []
  for (var i = 0; i < unit.length; i++) {
    if (returnArr[i][1] > 0){
    finalArr.push(returnArr[i])
  }
}
console.log(finalArr)

// sum values from finalArr to get exactChange total
  var exactChange = 0;
  for (var i = 0; i < finalArr.length; i++) {
    exactChange += finalArr[i][1];
  }
 console.log('exactChange =', exactChange)

// 1. Is there enough change/can u make exact change?
  if (cidSum < change || change != exactChange) {
    return {status: "INSUFFICIENT_FUNDS", change: []};
  }

// 2. Is cash in drawer sum == change due?
  if (cidSum == change) {
    return {status: "CLOSED", change: [cid]};
  }

// 3. Return array with change in decreasing value
  return {status: "OPEN", change: finalArr};
}

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]]));
// should return...
// {status: "OPEN", change: [["TWENTY", 60], ["TEN", 20], ["FIVE", 15], ["ONE", 1], ["QUARTER", 0.5], ["DIME", 0.2], ["PENNY", 0.04]]}

I’m refactoring your code, but its taking a little bit, but if you use toFixed in the loop where you create cidSum you can then check if cidSum === change and if thats true you can return the closed condition which will pass you one more test

1 Like