Data Structure & Algorithm

JS_Cash Register

Alledy 2019. 4. 6. 23:01
  • 문제

    price는 가격이고, cash는 손님이 지불한 돈, cid는 현재 남아있는 잔고이다. cid의 예시는 다음과 같다.

    // Example 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]]
    

    돈의 단위는 다음과 같다.

    Currency UnitAmount
    Penny$0.01 (PENNY)
    Nickel$0.05 (NICKEL)
    Dime$0.1 (DIME)
    Quarter$0.25 (QUARTER)
    Dollar$1 (DOLLAR)
    Five Dollars$5 (FIVE)
    Ten Dollars$10 (TEN)
    Twenty Dollars$20 (TWENTY)
    One-hundred Dollars$100 (ONE HUNDRED)
    • 만약 잔고가 부족하거나, 가지고 있는 돈의 단위로는 온전하게 거스름돈을 거슬러 줄 수 없다면 status는 'INSUFFICIENT_FUNDS'를 나타내고, change는 빈 어레이를 리턴한다.
    • 잔고가 있고 온전히 거슬러줄 수 있으면 status는 'OPEN'을 나타내고 change는 거스름돈을 배열로 나타내어 리턴한다. (큰 돈의 단위부터)
    • 거스름돈과 잔고가 완전히 일치할 경우 status는 'CLOSED'를 나타내고 change는 그 거스름돈을 배열로 나타낸다.
    // 예시
    // 잔고가 부족한 경우
    checkCashRegister(19.5, 20, [["PENNY", 0.01], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["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]]}.
    
    // 잔고가 있으나 돈의 단위가 안 맞아 온전히 거슬러 줄 수 없는 경우
    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: []}.
    
    // 잔고도 있고 온전히 거슬러 줄 수 있는 경우 1
    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]]) should return {status: "OPEN", change: [["QUARTER", 0.5]]}.
    
    // 잔고도 있고 온전히 거슬러 줄 수 있는 경우 2
    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]]}.
    
    

     

 

  • 내가 푼 답
function checkCashRegister(price, cash, cid) {
  var result = {status: '', change: []};
  var total = 0;
  var change = cash - price;
  var currency = [0.01, 0.05, 0.1, 0.25, 1, 5, 10, 20, 100];

  for(var i of cid) {
    total = total + i[1];
  }
  if(change > total) {
    result.status = 'INSUFFICIENT_FUNDS';
    return result; 
  }
  if(change === total) {
    result.status = 'CLOSED';
    result. change = cid;
    return result; 
  }
  if(change < total) {
    for(var j = currency.length - 1 ; j >=0 ; j--) {
      if(change > currency[j] && cid[j][1] !== 0) {
        var unit = cid[j][1] / currency[j];
        var n = 0;
        while(unit > 0 && change - currency[j] >= 0) {
          change = (change - currency[j]).toFixed(2)
          unit --;
          n++;
        }
        result.change.push([cid[j][0], n * currency[j]])
      }
    }
    if(change == 0) {
      result.status = 'OPEN';
      return result;
    } else {
      result.status = 'INSUFFICIENT_FUNDS';
      result.change = [];
      return result;
    }
  } 
}

덧셈이 제대로 안 되는 js 특성 때문에 toFixed를 사용했다. 밑의 솔루션은 Math.round를 사용했다.

 

  • Solution
var denom = [
  { name: 'ONE HUNDRED', val: 100.00},
  { name: 'TWENTY', val: 20.00},
  { name: 'TEN', val: 10.00},
  { name: 'FIVE', val: 5.00},
  { name: 'ONE', val: 1.00},
  { name: 'QUARTER', val: 0.25},
  { name: 'DIME', val: 0.10},
  { name: 'NICKEL', val: 0.05},
  { name: 'PENNY', val: 0.01}
];

function checkCashRegister(price, cash, cid) {
  var output = { status: null, change: [] };
  var change = cash - price;

  // Transform CID array into drawer object
  var register = cid.reduce(function(acc, curr) {
    acc.total += curr[1];
    acc[curr[0]] = curr[1];
    return acc;
  }, { total: 0 });

  // Handle exact change
  if (register.total === change) {
    output.status = 'CLOSED';
    output.change = cid;
    return output;
  }

  // Handle obvious insufficient funds
  if (register.total < change) {
    output.status = 'INSUFFICIENT_FUNDS';
    return output;
  }

  // Loop through the denomination array
  var change_arr = denom.reduce(function(acc, curr) {
    var value = 0;
    // While there is still money of this type in the drawer
    // And while the denomination is larger than the change remaining
    while (register[curr.name] > 0 && change >= curr.val) {
      change -= curr.val;
      register[curr.name] -= curr.val;
      value += curr.val;

      // Round change to the nearest hundreth deals with precision errors
      change = Math.round(change * 100) / 100;
    }
    // Add this denomination to the output only if any was used.
    if (value > 0) {
        acc.push([ curr.name, value ]);
    }
    return acc; // Return the current change_arr
  }, []); // Initial value of empty array for reduce

  // If there are no elements in change_arr or we have leftover change, return
  // the string "Insufficient Funds"
  if (change_arr.length < 1 || change > 0) {
    output.status = 'INSUFFICIENT_FUNDS';
    return output;
  }

  // Here is your change, ma'am.
  output.status = 'OPEN';
  output.change = change_arr;
  return output;
}

reduce를 쓰니까 더 깔끔해보인다.