2

how can I divide number (money) to x number equally the number could be with one or two decimal or without it

such as 1000 or 100.2 or 112.34
I want to be able to split that number into x part all of them equally, however if it's not odd number the extra number to the last one.

for example

3856 / 3
1285.33
1285.33
1285.34
ComFreek
  • 27,416
  • 16
  • 97
  • 150
MSB
  • 45
  • 1
  • 6

9 Answers9

12

review of other answers

After re-examining the solutions provided here, I noticed that they produce some strange results.

@GeorgeMauler's divideCurrencyEqually appears to work with the inputs provided in the original question. But if you change them up a bit, it produces a very strange result for …

// it appears to work
divideCurrencyEvenly(10,3)
=> [ '3.33', '3.33', '3.34' ]

// wups ...
divideCurrencyEvenly(10,6)
=> [ '1.67', '1.67', '1.67', '1.67', '3.32' ]

// hmm this one works ...
divideCurrencyEvenly(18,5)
=> [ '3.60', '3.60', '3.60', '3.60', '3.60' ]

// wups ...
divideCurrencyEvenly(100,7)
=> [ '14.29', '14.29', '14.29', '14.29', '14.29', '28.55' ]

While @Barmar's solution seems to perform a bit better … well a little bit …

// it appears to work
divide(10,3)
=> [ '3.33', '3.33', 3.34 ]

// acceptable, i guess
divide(10,6)
=> [ '1.66', '1.66', '1.66', '1.66', '1.66', 1.700000000000001 ]

// hmm, not so good, but still acceptable
divide(18,5)
=> [ '3.60', '3.60', '3.60', '3.60', 3.5999999999999996 ]

// as acceptable as the last two, i guess
divide(100,7)
=> [ '14.28', '14.28', '14.28', '14.28', '14.28', '14.28', 14.320000000000007 ]

Numbers like 3.5999999999999996 are mostly forgivable because that's how float arithmetic works in JavaScript.

But, what I find most disturbing about divide is that it's giving a mixed-type array (string and float). And because the numbers aren't rounded (to the correct precision) in the output, if you were to add the result up on paper, you will not arrive back at your original numerator input — that is, unless you do the rounding on your result.


and we're still fighting for equality …

My last grievance exists for both of the above solutions too. The result does not represent a distribution that is as close to equal as is possible.

If you divide 100 by 7 with a precision of 2, @Barmar's solution (with the rounding problem fixed) would give …

[ '14.28', '14.28', '14.28', '14.28', '14.28', '14.28', 14.32 ]

If these were people and the numbers represented monies, 1 person shouldn't pay 4 pennies more. Instead 4 people should pay 1 penny more …

[ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]

That's as close to equal as is possible


back to square one

I was dissatisfied with the solutions, so I made one of my own, distribute.

It has 3 parameters: precision p, divisor d, and numerator n.

  • it's returns a homogenous array — always an Array of Number
  • if you add them up, you will get a value exactly equal to your original numerator

It scales the numerator and finds the largest integer q where q * d <= n. Using modular division we know how many "slices" need to contribute q+1. Lastly, each q or q+1 is scaled back down and populates the output array.

const fill = (n, x) =>
  Array (n) .fill (x)

const concat = (xs, ys) =>
  xs .concat (ys)

const quotrem = (n, d) =>
  [ Math .floor (n / d)
  , Math .floor (n % d)
  ]

const distribute = (p, d, n) =>
{ const e =
    Math .pow (10, p)
    
  const [ q, r ] =
    quotrem (n * e, d)
    
  return concat
           ( fill (r, (q + 1) / e)
           , fill (d - r, q / e)
           )
}

console .log
  ( distribute (2, 3, 10)
    // [ 3.34, 3.33, 3.33 ]
    
  , distribute (2, 6, 10)
    // [ 1.67, 1.67, 1.67, 1.67, 1.66, 1.66 ]

  , distribute (2, 5, 18)
    // [ 3.6, 3.6, 3.6, 3.6, 3.6 ]

  , distribute (2, 7, 100)
    // [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]
  )

You'll see that I made precision a parameter, p, which means you can control how many decimal places come out. Also note how the largest difference Δ between any number in the result is Δ <= 1/10^p

distribute (0, 7, 100)
=> [ 15, 15, 14, 14, 14, 14, 14 ] // Δ = 1

distribute (1, 7, 100)
=> [ 14.3, 14.3, 14.3, 14.3, 14.3, 14.3, 14.2 ] // Δ = 0.1

distribute (2, 7, 100)
=> [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ] // Δ = 0.01

distribute (3, 7, 100)
=> [ 14.286, 14.286, 14.286, 14.286, 14.286, 14.285, 14.285 ] // Δ = 0.001

distribute (4, 7, 100)
=> [ 14.2858, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857 ] // Δ = 0.0001

distribute can be partially applied in meaningful ways. Here's one way petty people could use it to precisely split the bill at a restaurant …

// splitTheBill will use precision of 2 which works nice for monies
const splitTheBill = (people, money) =>
  distribute (2, people, money)

// partyOfThree splits the bill between 3 people
const partyOfThree = money =>
  splitTheBill (3, money)

// how much does each person pay ?
partyOfThree (67.89)
=> [ 18.93, 18.93, 18.92 ]

And here's an effective way to divide people into groups — while being careful not to divide an individual person — which typically results in death …

// p=0 will yield only whole numbers in the result
const makeTeams = (teams, people) =>
  distribute (0, teams, people)

// make 4 teams from 18 people
// how many people on each team?
makeTeams (4, 18)
=> [ 5, 5, 4, 4 ]

Thank you
  • 107,507
  • 28
  • 191
  • 224
  • Oh whew...after staring at this for like 15 minutes I realize it's not my solution that had the typing issues. I had no idea how mine would get the wrong type. But yeah, perfectly willing to acknowledge there might be some bugs in mine. I really like the recursion approach with generators for this but I'd probably TDD it in reality. Good solution! – George Mauer Aug 12 '16 at 20:24
  • 1
    @George, I also liked the recursive approach but found that it wasn't needed once `q` and `r` were computed just one time. If we were to implement `quotrem` or `fill` by hand (instead of relying on `Math.floor` or `Array.prototype.fill`) we could then get back some of the sexy recursion. – Thank you Aug 12 '16 at 20:30
  • For those not supporting E6; – Louis Oct 17 '16 at 23:17
  • @Louis I added an ES5 version – Thank you Oct 18 '16 at 02:34
  • Works exactly as I was expecting! Thanks. – jegadeesh May 30 '19 at 12:17
2
function divide(numerator, divisor) {
    var results = [];
    var dividend = (Math.floor(numerator/divisor*100)/100).toFixed(2); // dividend with 2 decimal places
    for (var i = 0; i < divisor-1; i++) { 
        results.push(dividend); // Put n-1 copies of dividend in results
    }
    results.push(numerator - (divisor-1)*dividend); // Add remainder to results
    return results;
}
Barmar
  • 596,455
  • 48
  • 393
  • 495
  • I was working on a very similar solution. One thing that I wanted to point out is that `toFixed(2)` doesn't truncate, it rounds. And it's possible for the numberator to be less than the re-calculated total if the dividends are rounded up. I'm not sure if that case will ever exist. But I thought I would throw it out there. – Smeegs Oct 31 '13 at 20:25
  • 1
    @Smeegs Thanks. I've changed it to multiply by 100, get the integer floor, then divide by 100 to get back to 2 decimal places. – Barmar Oct 31 '13 at 20:29
  • Thank you so much this one also works . THANKS again – MSB Nov 01 '13 at 11:55
0

Sounds like a pretty straightforward loop/recursion.

Here you go

function divideEvenly(numerator, minPartSize) {
  if(numerator / minPartSize< 2) {
    return [numerator];
  }
  return [minPartSize].concat(divideEvenly(numerator-minPartSize, minPartSize));
}

console.log(divideEvenly(1000, 333));

To get it to be two decimals of currency multiply both numbers by 100 before calling this function then divide each result by 100 and call toFixed(2).

Like so

function divideCurrencyEvenly(numerator, divisor) {
  var minPartSize = +(numerator / divisor).toFixed(2)
  return divideEvenly(numerator*100, minPartSize*100).map(function(v) {
    return (v/100).toFixed(2);
  });
}


console.log(divideCurrencyEvenly(3856, 3));
//=>["1285.33", "1285.33", "1285.34"]
George Mauer
  • 103,465
  • 117
  • 349
  • 581
  • Where does the input `333` come from? Isn't that something that needs to be computed? – Barmar Oct 31 '13 at 20:31
  • @Barmar yeah `var minPartSize = Math.floor(numerator / divisor)` – George Mauer Oct 31 '13 at 20:36
  • @naomik will be even nicer once we get es6 generators – George Mauer Oct 31 '13 at 20:37
  • @naomik [here you go](http://jsbin.com/uLaZUBa/8/edit?js) this will only work in Firefox or experimental node and there is still some fluctuation as to exact API - but it will look roughly like this. – George Mauer Oct 31 '13 at 21:56
  • some funky things happen when trying to divide an amount that is an uneven number, or one that has decimals, but pretty decent otherwise. – Grapho Oct 13 '14 at 16:43
  • @GeorgeMauer I had to respectfully rescind my comment about this implementation after finding some serious bugs in it. Details in the answer I provided. – Thank you Aug 12 '16 at 20:03
0

Since we are talking about money, normally a few cents different doesn't matter as long as the total adds up correctly. So if you don't mind one payment possibly being a few cents greater than the rest, here is a really simple solution for installment payments.

function CalculateInstallments(total, installments) {
  // Calculate Installment Payments
  var pennies = total * 100;
  var remainder = pennies % installments;
  var otherPayments = (pennies - remainder) / installments;
  var firstPayment = otherPayments + remainder;

  for (var i = 0; i < installments; i++) {
    if (i == 0) {
      console.log("first payment = ", firstPayment / 100);
    } else {
      console.log("next payment = ", otherPayments / 100);
    }
  }
}

Since you said you wanted the highest payment at the end you would want to modify the if statement in the for loop: if (i==installments-1) { //last payment }

cmartin
  • 2,479
  • 19
  • 26
0

There is an issue on @user633183's distribute but only happen when the divider is lower than 3.

distribute(2, 2, 560.3)
// [ '280.15' , '280.14']

distribute(2, 1, 74.10)
// [ '74.09' ]

distribute(2, 1, 74.60)
// [ '74.59' ]

I rewrote the answer by Guffa into javascript

const distribute = (precision, divider, numerator) => {
const arr = [];
  while (divider > 0) {
    let amount = Math.round((numerator / divider) * Math.pow(10, precision)) / Math.pow(10, precision);
    arr.push(amount);
    numerator -= amount;
    divider--;
  }
  return arr.sort((a, b) => b-a);
};

Here are the results

distribute(0, 7, 100)
=> [ 15, 15, 14, 14, 14, 14, 14 ]

distribute(1, 7, 100)
=> [ 14.3, 14.3, 14.3, 14.3, 14.3, 14.3, 14.2 ]

distribute(2, 7, 100)
=> [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]

distribute(3, 7, 100)
=> [ 14.286, 14.286, 14.286, 14.286, 14.286, 14.285, 14.285 ]

distribute(4, 7, 100)
=> [ 14.2858, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857 ]

// and of course

distribute(2, 2, 560.3)
=> [ 280.15, 280.15 ]

distribute(2, 1, 74.10)
=> [ 74.1 ]

distribute(2, 1, 74.60)
=> [ 74.6 ]
OysterD3
  • 195
  • 2
  • 14
0

please check the below code,

function Dividently(Amount, Quantity) {
        var m = Amount* 100,
            n = m % Quantity,
            v = Math.floor(m / Quantity) / 100,
            w = Math.floor(m / Quantity+ 1) / 100;

        for (var i = 0, out = new Array(Quantity); i < Quantity; ++i) {
            out[i] = i < n ? w : v;
        }
        return out;
    }    

 var arr = Dividently(3856, 3);    

Math.floor() - Round a number downward to its nearest integer

0

If anyone is looking for a randomly distributed vanilla JS solution here is mine:

function fairDivision(resolution, numerator, denominator) {
    // preserves numerator(n) integrity when dividing into denominator(d) bins
    // remainders are randomly distributed into sequential bins 
    // resolution is number of significant digits after decimal
    // (-'ve resolution for insignificant digits before decimal).
    const n = numerator * Math.pow(10, resolution);
    const remainder = n % denominator;
    const base = (n - remainder) / denominator;  
    const offset = Math.floor(Math.random()*denominator); // index of first 'round-up' bin

    let arr = []
    let low= (offset + remainder) % denominator;

    let a; let b; let c = (low < offset); //logical units
    for (let i=0; i < denominator; i++) {
        a = (i < low);
        b = (i >= offset);
        if ((a && b) || (a && c) || (b && c)) {
            arr.push(base +1)
        } else{
            arr.push(base)
        }
        arr[i] = arr[i] / Math.pow(10, resolution);
    }
    return arr;
}
0

This is based in @Thank_you's conclusions, just to divide portions with no decimals, based in equality:

//n=how many groups, lot = total units
function makeTeams2(n,lot){
  var div = lot/n;
  var portion = Math.floor(div);
  var remains = Math.round(n*((div) % 1));
  var arr = [];
  for(var i=0; i<n; i++) arr.push(portion);
  for(var i=0; i<remains; i++) arr[i] = arr[i] + 1;
  return arr;
}

// >> makeTeams2(7,100);
// >> [15,15,14,14,14,14,14]

This is a little faster than @Thank_you's "makeTeams(teams,people)" constant but I'm accepting performance improvements since I'm a beginner, I just did this on my own way and wanted to share.

-1
<button onclick="myFunction(3856,3)">Try it</button>

function myFunction(number,divide) {
        var money = number / divide;
        money = Math.ceil(money * 100) / 100;
        alert(money);   //1285.34
    }

http://jsbin.com/ucuFuLa

user2511140
  • 1,484
  • 2
  • 24
  • 31