Cash Register - want to use reduce ()

In an attempt to up my JS game, I want to get away from for loops and use filter(), map(), reduce() instead.

To that end, as a first step in my code, I want to add up all the values in the given array, cid. I am using reduce() to add all those values up to get the total value of cash in drawer. But am getting unexpected results. Do I need to use map() to pull out all values from cid first THEN reduce()?

Can anyone help please?

Thank you.

Your code so far

function checkCashRegister(price, cash, cid) {

  //Calc total of cash in register.

  let totalCashRegister = cid.reduce((arr, sum) => {
    return sum + arr[1];
  })

  console.log(totalCashRegister);


  //Calc change due = cash - price
    //If change due > cash in register, return {status: "INSUFFICIENT_FUNDS", change: []}
    //If change due < cash in register,
      //and can return exact change
        //and there is still change left in drawer: return {status: "OPEN", change: [...]} with the change due in coins and bills, sorted in highest to lowest
        //but there is no more change left in drawer: return {status: "CLOSED", change: [...]} with cash-in-drawer as the value
      //but cannot return exact change, return {status: "INSUFFICIENT_FUNDS", change: []}
}

Link to the challenge:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/javascript-algorithms-and-data-structures-projects/cash-register

The first two arguments passed to the callback are accumulator and current value respectively, and the callback should return the new value of the accumulator.

Try reading again on the reduce method, the arguments of your callback functions are not arrays, so using arr[1] is giving an undefined probably, and it’s breaking whatever you wanted to do

Reduce Method

1 Like

Thanks @leahleen. That worked!

Now I am trying to get through some map() logic.

1 - code has returned an array that holds the currency note and quantity, i.e. changeVol
2 - Now I want to go through cid array using map() to return a new cidLeft array that will change the values of notes left depending on changeVol.
3 - I got down to the level at which I am manipulating the elements of the appropriate nested array. But I don’t actually know what to do with it. From a pseudo-code perspective, I want to look at each nested array in cid, compare it to each nested array in changeVol and reduce quantity of each currency note as needed. So my question is: am I going about it the right way, i.e. using map() twice in a row? If so, how can I access the correct value of changeVol array in the 2nd map()…?

Given the following test-case, my changeVol only has one nested array but that could obviously change to multiple nested arrays with another testcase:

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]]);

Code so far:

function checkCashRegister(price, cash, cid) {

  //Calc total of cash in register.

  let currEquiv = [["ONE HUNDRED", 100.00], ["TWENTY", 20.00],  ["TEN", 10.00], ["FIVE", 5.00], ["ONE", 1.00], ["QUARTER", 0.25], ["DIME", 0.1],["NICKEL", 0.05],["PENNY", 0.01]];


  let totalCashRegister = cid.reduce((total, item) => {
    return total + item[1];
  }, 0)

  totalCashRegister = (Math.round((totalCashRegister) * 100) / 100);


  //Calc change due = cash - price
  let changeDue = cash - price;

  //Calc change in currency:
  let changeVol = [];

  for(var i = 0; i < currEquiv.length; i++){
      if(changeDue % currEquiv[i][1] === 0 && changeDue != 0){
        changeVol.push(currEquiv[i][0],(changeDue/currEquiv[i][1]));
        changeDue -=  (currEquiv[i][1] * (changeDue/currEquiv[i][1]))
      }
  }

  console.log("This is changeVol: " + changeVol);

  //Calc change left in drawer:
  let cidLeft = cid.map(function(item){
      return item.map(function(anotherItem, index){
        if(changeVol.indexOf(anotherItem) >= 0){
          //do somthing
        }
      });
  });

  console.log("This is cidLeft: " + cidLeft);
}

You don’t actually want to map() the cid array – it may work better to map() your currEquiv array and, within that, simply cid.filter(...) to get the one drawer with a matching currency name.

That said, here’s the logic I used in mine to use map() and filter() effectively. It actually makes for a pretty streamlined solution. Note that this was taken straight from my code, so it’s all the commentary I put inline. :wink:

  /***
   * So the logic of going through the register using map() and filter()
   *   might look something like this:
   *
   * 1. map() though the currEquiv array, as that will determine each
   *    "drawer" in our till.
   * 2. Find the currency in cid with the matching [0]th element to
   *    the current currEquiv, as that is how much is actually in the
   *    "drawer".
   * 3. Determine the actual number of bills in the current "drawer". We
   *    get this by dividing the current cid[1] by the currency value.
   * 4. Determine how many times the current changeDue is divisible by
   *    the current drawer value -- that is possible quantity.
   * 5. Determine the actual quantity from this drawer. This is whichever
   *    is LESS between the values from steps 3 and 4.
   * 6. Reduce the changeDue by the actual quantity * the currency value.
   * 
   * 7. Return from this iteration of map an array, containing the currency
   *    name and the actual quantity * the currency value.
   *
   * ONCE ALL THAT IS DONE, the list should still be filtered -- We don't
   *    return drawers with a zero value in the change array.
   ***/
1 Like

So I am trying to code step by step while also taking the time to understand each step. If I am completely honest, I am not able to grasp all of the logic in the pseudo-code above. But hoping that if I follow all the steps, it will become clearer to me.

I am getting lost with step 3 and on. (Feel free to ignore the cidValues and numberBills arrays. I only added them so I could see what the result of my filter loop was in console.log.)

But - does what I have done so far make sense - at least up to step 3?

function checkCashRegister(price, cash, cid) {

  //Calc total of cash in register.

  let currEquiv = [["ONE HUNDRED", 100.00], ["TWENTY", 20.00],  ["TEN", 10.00], ["FIVE", 5.00], ["ONE", 1.00], ["QUARTER", 0.25], ["DIME", 0.1],["NICKEL", 0.05],["PENNY", 0.01]];


  let totalCashRegister = cid.reduce((total, item) => {
    return total + item[1];
  }, 0)

  totalCashRegister = (Math.round((totalCashRegister) * 100) / 100);


  //Calc change due = cash - price
  let changeDue = cash - price;
  let changeVol = [];

  //Calc change in currency:
  for(var i = 0; i < currEquiv.length; i++){
      if(changeDue % currEquiv[i][1] === 0 && changeDue != 0){
        changeVol.push(currEquiv[i][0],(changeDue/currEquiv[i][1]));
        changeDue -=  (currEquiv[i][1] * (changeDue/currEquiv[i][1]))
      }
  }

  console.log("This is changeVol: " + changeVol);

  //Calc change left in drawer:
  let cidValues = [];
  let numberBills = [];

  let newCID = currEquiv.map(function(item, index){    //Go through each nested array in currEquiv: ["ONE HUNDRED", 100.00] then ["TWENTY", 20.00] etc etc
    return cid.filter(function(anotherItem,anotherIndex){  //Within each nested array of currEquiv, filter out cid currency value where currEquiv's nested array's [0] === cid[anotherItem][0]
      if (cid[anotherIndex][0] === currEquiv[index][0]){
        cidValues.push(cid[anotherIndex][1]);
        numberBills.push(cid[anotherIndex][1]/currEquiv[index][1])
        return cidValues;
        return numberBills;
      }
    })
  });

  console.log(cidValues);
  console.log(numberBills);

So the first thing I might suggest, before you even get to map() or reduce(), you can already eliminate two branches of the big picture logic. Before you hit the map function, you already have your totalCashRegister and your changeDue, right? So, before you even get into the map, you can eliminate two branches of the register logic:

if (totalCashRegister < changeDue) {
  // Here, we don't have the cash in the drawer to pay changeDue.
  //   We can simply return the INSUFFICIENT_FUNDS and []
} else if (totalCashRegister === changeDue){
  // And here, we have EXACTLY what we need to pay changeDue.
  //   simply return CLOSED and cid
}
// Now, we get into that funky map thing...

This leaves you with exactly two cases to handle: if the change in the drawer won’t let us give exact change (for example, too many dimes and not enough pennies), and a valid change transaction.

Here’s how the logic of the map() might look:

let changeToReturn = currEquiv.map(function(currency){
  // First, filter the cid to get the drawer with the same name or [0] value.
  //   How would you find that? Replace the asterisks below.
  let drawer = cid.filter(function(drawerItem){ return drawerItem[0] == ***** })
  // Next, how many units are in this drawer? We can find that by dividing
  //  the drawer's value by the currency value. Again, replace the asterisks.
  let drawerUnits = *****;
  // What is the MOST of this particular currency we can return within the
  //  current changeDue? We find THAT by dividing changeDue by the current value.
  let possibleUnits = Math.floor( ***** );
  // Now, how many units do we want to return? We can't return more than we have,
  //  so it will be whichever is LESS: drawerUnits or possibleUnits. Asterisks...
  let actualUnits = (*******) ? drawerUnits : possibleUnits;
  // What is the actual value we're returning? That would be the actualUnits times
  //   the currency value. Asterisks.
  let actualValue = Number( (******).toFixed(2) ); // Fix for floating point oddity

  // We need to reduce changeDue by that actualValue.
  changeDue = Number( (****).toFixed(2) );
  
  // Now, what do we want to return here? The challenge is looking for an array,
  //  with [0] being the currency name and [1] being the actualValue.
  return [ ******* ]
}).filter(function(item){
  // NOT DONE YET. Heh. Now, we want to keep only those items with a non-zero
  //  value in the [1]th position.
})

I’ve left the asterisks as points you’d work your code in. This could all be done much more succinctly, but breaking it down step-by-step like this shows the logic flow in a readable and (hopefully) understandable way.

And by the by, that for loop you have in there is not going to help with this. Once you do the map/reduce, that will be redundant.

--------UPDATE TO ANYONE REVIEWING MY COMMENTS BELOW: I DECIDED TO GO WITH FOR LOOPS FOR NOW SO NOT AS URGENT TO GET MAP() FILTER() REDUCE() TO WORK FOR NOW----

I had to tweak logic a little.

I went through each of the steps using numbers from a real testcase:

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]]);

1- Go through each element of currEquiv //[“QUARTER”, 0.25]
2- If changeDue > 0**
3- Get CID drawer that has same name as currEquiv name // [“QUARTER”, 4.25]
4- How many quarters in CID drawer? Div CID drawer value by currency value // 4.25/.25 = 17
5- How many of these bills can we use to cover as much of changeDue as possible? // 0.5/0.25 = 2
6- And is that number greater than or equal to 1?***
a. If yes, return lesser of drawerUnits vs possibleUnits: 2
b. Actual returned value: 2 * .25 = 0.5
c. Reduce changeDue by actual value returned: 0.5 – 0.5 = 0
d. Return [CID drawer name, possibleBills]

e. If not, move to next element in currEquiv. If only returns these, then cannot give exact change, I think.

_****Added this b/c as you go through each element in map(), we want to make sure that there is any changeDue remaining._**

***The reason I did this was b/c when this map() first starts, currEquiv will be [“ONE HUNDRED”, 100.00] and therefore the code will run like this:

1- Go through each element of currEquiv //[“ONE HUNDRED”, 100.00]
2- If changeDue > 0
3- Get CID drawer that has same name as currEquiv name // [“ONE HUNDRED”, 100]
4- How many $100 bills in CID drawer? Div CID drawer value by currency value // 100/100 = 1
5- How many of these bills can we use to cover as much of changeDue as possible? // 0.5/100 = 0.005
6- And is that number greater than or equal to 1? //If I didn’t check this, I would be trying to give 0.005 of a 100 dollar bill…
a. If yes, return lesser of drawerUnits vs possibleUnits: 2
b. Actual returned value: 2 * .25 = 0.5
c. Reduce changeDue by actual value returned: 0.5 – 0.5 = 0
d. Return [CID drawer name, possibleBills]

e. If not, move to next element in currEquiv. If only returns these, then cannot give exact change, I think.

1 Like

Well, as to step 6, that was why my most possible bills check uses Math.floor() - any fraction of an integer gets rounded down. Thus .005 of $100 or .9992375 of $100 should BOTH round down to 0.

Thanks for all your help with this. I think I need to practice easier reduce(), map(), filter() solutions. I was a little hopeless here. But I’m gonna add that to my list of JS concepts I need to focus on. Thanks again!