1

This function which will execute when you enter on input field then it will calculate how much money do you have to return with x type to customer.

But getting stack size error sometime for 6 digit and everytime for 7 digit.

Reproduce: put 1234567 in input box and check the console.

Function:

let returnList = [];
let predictorList = [
    100, 50, 20, 10, 5, 1, 0.5, 0.25, 0.1, 0.05, 0.01,
  ];
let total = 11.23

function isRemainingMoney(value) {
    let remainingValue = value;

    // when remaning value becomes zero then return the returnlist
    if (remainingValue == 0) {
      return returnList;
    }

    for (let pRed of predictorList) {
      /* remainingValue is greater than predictor value then push it 
      eg:  41.33 > 20
           21.33 > 20
           1.33 > 1
           0.33 > 0.25
           0.08 > 0.05
           0.03 > 0.01 * 3 times
      */
      if (remainingValue >= pRed) {
        const isPredExist = returnList.find(
          (pItem) => +pItem.money == +pRed
        );
        if (!!isPredExist) {
          isPredExist.count += 1;
          isPredExist.total = isPredExist.total + +pRed;
        } else {
          returnList.push({
            type: pRed,
            money: pRed,
            count: 1,
            total: pRed,
          });
        }
        remainingValue = +remainingValue.toFixed(2) - pRed;
        break;
      }
    }
    // recursive call the same method untill remainivalue becomes zero.
    return isRemainingMoney(+remainingValue.toFixed(2));
  }

document.querySelector('input').addEventListener('change', (event) => {
   if(!!event.target.value) {
        returnList.length = 0;
        returnList = isRemainingMoney(+event.target.value - total);
        console.log(returnList, 'returnList');
   }
   
})

Playground: https://jsbin.com/kuwomalare/edit?html,js,console,output

Current Output From real application: Current Output From real application:

Indraraj26
  • 1,726
  • 1
  • 14
  • 29
  • Does this answer your question? [Maximum call stack size exceeded error](https://stackoverflow.com/questions/6095530/maximum-call-stack-size-exceeded-error) – Liam Feb 09 '22 at 13:56
  • 2
    I guess `if (remainingValue == 0) {` is never true. – 001 Feb 09 '22 at 13:57
  • Simple debugging would of quite quickly yielded the answer here – Liam Feb 09 '22 at 13:58
  • @JohnnyMopp but it works fine if you try with lower number like put 20 in input box – Indraraj26 Feb 09 '22 at 13:58
  • 2
    Floating point numbers can be awkward in any language, not just JS. `== 0` could be failing like pointed out, and it's likely ok for smaller number because the bigger your number, the more error can creep into the floating point precision. There are multiple ways to solve this, use a BCD type lib, but a simple solution is to keep all the numbers as integers, eg. multiply by 100 first, do your recursion and then divide by 100 at the end. – Keith Feb 09 '22 at 14:01
  • @Keith, I agree with you but it is really hard to debug, I tried with Math.floor on remainingValue but still i do get stack size error – Indraraj26 Feb 09 '22 at 14:04
  • 1
    Looks like you have too many recursive calls. Try converting the algorithm to an iterative process. I tried it with `1234567` and it crashed after 6462 recursive calls. – 001 Feb 09 '22 at 14:06
  • @JohnnyMopp, let me know if you have any clue to turn down this algo little shorter on recursive calls. added snapshot for reference. – Indraraj26 Feb 09 '22 at 14:17
  • You know I thought Tail Call Elimination were part of the web, but it looks like it's not -> https://stackoverflow.com/questions/54719548/tail-call-optimization-implementation-in-javascript-engines So like pointed out converting to an iterative approach looks good, or the trampoline technique in that link looks like a nice option too. – Keith Feb 09 '22 at 14:26
  • 1
    Thank you @Keith for suggestion, I will check that out – Indraraj26 Feb 09 '22 at 14:32

1 Answers1

1

You end up having way too many recursive calls when the input value is large. It should be fairly straight forward to convert from recursive to iterative using a while loop. The only issue I ran into was floating point not getting down to 0 (like mentioned in the comment by @Keith). When doing money calculations, it is usually best to use integers. Use the smallest denomination in the currency. For example, in US currency, that would be cents. Only convert to decimal (or dollars in this case) when displaying the values.

I have also simplified your calculations a bit. Because of these changes, you could actually use recursion now, if you want since the maximum level of recursion now is predictorList.length.

let predictorList = [
  10000, 5000, 2000, 1000, 500, 100, 50, 25, 10, 5, 1
];
let total = 709;

function isRemainingMoney(value) {
  let returnList = [];

  // when remaning value becomes zero then return the returnlist
  while (value != 0) {
    for (let pRed of predictorList) {
      if (value >= pRed) {
        returnList.push({
            money: pRed / 100,
            count: Math.floor(value / pRed),
          });
        value %= pRed;
      }
    }
  }
  return returnList;
}
document.querySelector('input').addEventListener('change', (event) => {
  if (!!event.target.value) {
    let returnList = isRemainingMoney(+event.target.value * 100 - total);
    console.log(returnList, 'returnList');
  }

})
<input type="number">
001
  • 13,291
  • 5
  • 35
  • 66