JavaScript Algorithms and Data Structures Projects: Cash Register
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.
Currency Unit Amount
Penny $0.01 (PENNY)
Nickel $0.05 (NICKEL)
Dime $0.1 (DIME)
Quarter $0.25 (QUARTER)
Dollar $1 (ONE)
Five Dollars $5 (FIVE)
Ten Dollars $10 (TEN)
Twenty Dollars $20 (TWENTY)
One-hundred Dollars $100 (ONE HUNDRED)
See below for an example of a cash-in-drawer array:
[
["PENNY", 1.01],
["NICKEL", 2.05],
["DIME", 3.1],
["QUARTER", 4.25],
["ONE", 90],
["FIVE", 55],
["TEN", 20],
["TWENTY", 60],
["ONE HUNDRED", 100]
]
Duration: 1 hour. See video: https://www.youtube.com/watch?v=rx7IUYq1wkU
const UNIT_VALUES = Object.freeze({ 'ONE HUNDRED': 100, TWENTY: 20, TEN: 10, FIVE: 5, ONE: 1, QUARTER: 0.25, DIME: 0.1, NICKEL: 0.05, PENNY: 0.01 }); function monify(obj) { if (typeof obj === 'number') { return Number(obj.toFixed(2).replace('.', '')); } else if (obj === undefined || obj === null || typeof obj === 'string' || obj instanceof Date) { return obj; } else if (Array.isArray(obj)) { return obj.map(el => monify(el)); } else if (typeof obj === 'object') { return Object.entries(obj).reduce((acc, [key, value]) => ({ ...acc, [key]: monify(value) }), {}); } else { throw new Error('Invalid Data', obj); } } console.log(monify(new Date())); function floatify(obj) { if (typeof obj === 'number') { return Number(obj) / 100; } else if (obj === undefined || obj === null || typeof obj === 'string'|| obj instanceof Date) { return obj; } else if (Array.isArray(obj)) { return obj.map(el => floatify(el)); } else if (typeof obj === 'object') { return Object.entries(obj).reduce((acc, [key, value]) => ({ ...acc, [key]: floatify(value) }), {}); } else { throw new Error('Invalid Data', obj); } } function checkCashRegister(argPrice, argCash, argCid, argUnitValues = UNIT_VALUES) { const { argPrice: price, argCash: cash, argCid: cid, argUnitValues: UNIT_VALUES } = monify({ argPrice, argCash, argCid: [...argCid], argUnitValues }); // From here onwards, we can pretend that we are still operating on original values, courtesy of monify function. // All values are all scaled to 100 times for us, to prevent inaccurate representation of cents in floating point number. // No need to change the code's logic. // If we are not using monify, floating point fraction produces inaccurate result, e.g., // console.log(100 - 3.26 - 60); // produces incorrect result: 36.739999999999995 // Correct result is: 36.74 cid.sort(([aUnit], [bUnit]) => UNIT_VALUES[bUnit] - UNIT_VALUES[aUnit]); const change = cash - price; let uncollectedChange = change; const collectedChange = []; for (const [drawerUnit, drawerAmount] of cid) { const unitValue = UNIT_VALUES[drawerUnit]; if (unitValue > change) { collectedChange.push([drawerUnit, 0]); } else { if (uncollectedChange >= drawerAmount) { collectedChange.push([drawerUnit, drawerAmount]); uncollectedChange -= drawerAmount; } else { const toDeduct = Math.floor(uncollectedChange / unitValue) * unitValue; collectedChange.push([drawerUnit, toDeduct]); uncollectedChange -= toDeduct; } } } if (uncollectedChange > 0) { return { status: 'INSUFFICIENT_FUNDS', change: [] }; } const totalDrawerAmount = cid.reduce((sum, [, drawerAmount]) => sum + drawerAmount, 0); const status = totalDrawerAmount - change === 0 ? 'CLOSED' : 'OPEN'; return floatify({ status, change: collectedChange .filter(([, ccValue]) => status === 'OPEN' && ccValue > 0 || status === 'CLOSED') .sort(([aUnit, aValue], [bUnit, bValue]) => bValue - aValue || UNIT_VALUES[aUnit] - UNIT_VALUES[bUnit]) }); } [ 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(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]]), checkCashRegister(19.5, 20, [["PENNY", 0.01], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]), checkCashRegister(19.5, 20, [["PENNY", 0.01], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 1], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]), checkCashRegister(19.5, 20, [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]), ].forEach(result => console.log(result));
Output:
{ status: 'OPEN', change: [ [ 'QUARTER', 0.5 ] ] } { status: 'OPEN', change: [ [ 'TWENTY', 60 ], [ 'TEN', 20 ], [ 'FIVE', 15 ], [ 'ONE', 1 ], [ 'QUARTER', 0.5 ], [ 'DIME', 0.2 ], [ 'PENNY', 0.04 ] ] } { status: 'INSUFFICIENT_FUNDS', change: [] } { status: 'INSUFFICIENT_FUNDS', change: [] } { status: 'CLOSED', change: [ [ 'PENNY', 0.5 ], [ 'NICKEL', 0 ], [ 'DIME', 0 ], [ 'QUARTER', 0 ], [ 'ONE', 0 ], [ 'FIVE', 0 ], [ 'TEN', 0 ], [ 'TWENTY', 0 ], [ 'ONE HUNDRED', 0 ] ] }
No comments:
Post a Comment