3
\$\begingroup\$

I'm writing a function divideMoney that takes the value of a sum of money and divides it according to some percentages, then rounds each division to the nearest denomination.

For example:

//divideMoney(sum, percentages, denomination);

divideMoney(102, [0.5, 0.30, 0.20], 0.25);

This should divide the sum of $102 to 3 divisions, first is 50% ($51), second is 30% ($30.6) and the third is 20% ($20.4).

But because the smallest accepted denomination is the quarter ( 0.25 ), so $30.6 should be $30.50 instead, and $20.40 should be $20.50 as well. So the expected result is [51, 30.5, 20.5].

Here is what I tried

Original Code

function divideMoney(sum, percentages, denomination) {
    var result, diff, resultSum;
    result = [];
    diff = 0;
    percentages.forEach(function (percent) {
        var div, fraction, mod;
        div = (percent * sum) + diff;
        fraction = div - Math.trunc(div);
        if (fraction > 0) {
            mod = fraction % denomination;
            diff += mod;
            div -= mod;
        }
        result.push(div);
    });
    resultSum = result.reduce(function(a, b){ return a + b; }, 0);
    if (resultSum < sum) {
        result.push(sum - resultSum);
    }
    return result;
}

Update (fix rounding)

function divideMoney(sum, percentages, denomination) {
    var result = [], remainder = sum, part, i;
    for(i = 0; i < percentages.length - 1; i++) {
        part = percentages[i] * sum;
        if (part % 1) {
            part = Math.round(part/denomination)*denomination;
        }
        remainder -= part; 
        result.push(part);
    }
    result.push(remainder);
    return result;
}
\$\endgroup\$
9
  • \$\begingroup\$ Does your code work as expected? It sounds like it's not doing what it should. \$\endgroup\$
    – Pimgd
    Mar 3 '16 at 11:11
  • \$\begingroup\$ I think it works well in many straightforward cases, however I'm not sure. \$\endgroup\$
    – KeyBored
    Mar 3 '16 at 11:52
  • \$\begingroup\$ Add more unit tests and cover boundary conditions. \$\endgroup\$
    – Frank C.
    Mar 3 '16 at 12:15
  • \$\begingroup\$ Ah yes, I can see in your code you do things with denomination and modulus, from the way I read your question it was like "it divides properly but doesn't handle denomination properly" \$\endgroup\$
    – Pimgd
    Mar 3 '16 at 12:39
  • 1
    \$\begingroup\$ I'm puzzled by the method: when a primarily computed percentage does not match denomination, it's always decreased to the next one down. Shouldn't be rather increased/decreased to the closer one, like it usually apply when rounding? \$\endgroup\$
    – cFreed
    Mar 3 '16 at 12:57
2
\$\begingroup\$

I can make infinite money (well, as much as will fit in a javascript number) with your code!

var money = 10;
while(true){
    money = divideMoney(money, [3, 0], 1)[0];//muhahahahah infinite money
}

Which divides the money like so: 1 for me, 1 for myself, 1 for I, none for you. 1 for me...

I'd recommend in this case to fall back on relative percentages: that is, sum the percentages and then set the cut to be the percentage of the summed percentage. So 10, 5, 5 = 20 total, 10/20 = 0.5, 5/20 = 0.25, so it's 0.50, 0.25, 0.25.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your answer but in my case I always assume that the provided inputs are valid, so I don't need to validate the inputs. \$\endgroup\$
    – KeyBored
    Mar 3 '16 at 13:58
  • \$\begingroup\$ The percentages array will always contain values between 0 and 1 (exclusively), and their sum will be always 1. \$\endgroup\$
    – KeyBored
    Mar 3 '16 at 14:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.