Created
February 29, 2024 18:24
-
-
Save jerolan/c018e19210806be35fa6cab5c2240cd1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* From https://v1.dinerojs.com/dinero.js | |
* Allocates the amount of a Dinero object according to a list of ratios. | |
* | |
* Sometimes you need to split monetary values but percentages can't cut it without adding or losing pennies. | |
* A good example is invoicing: let's say you need to bill $1,000.03 and you want a 50% downpayment. | |
* If you use {@link module:Dinero~percentage percentage}, you'll get an accurate Dinero object but the amount won't be billable: you can't split a penny. | |
* If you round it, you'll bill a penny extra. | |
* With {@link module:Dinero~allocate allocate}, you can split a monetary amount then distribute the remainder as evenly as possible. | |
* | |
* You can use percentage style or ratio style for `ratios`: `[25, 75]` and `[1, 3]` will do the same thing. | |
* | |
* Since v1.8.0, you can use zero ratios (such as [0, 50, 50]). If there's a remainder to distribute, zero ratios are skipped and return a Dinero object with amount zero. | |
* | |
* @param {Number[]} ratios - The ratios to allocate the money to. | |
* | |
* @example | |
* // returns an array of two Dinero objects | |
* // the first one with an amount of 502 | |
* // the second one with an amount of 501 | |
* Dinero({ amount: 1003 }).allocate([50, 50]) | |
* @example | |
* // returns an array of two Dinero objects | |
* // the first one with an amount of 25 | |
* // the second one with an amount of 75 | |
* Dinero({ amount: 100 }).allocate([1, 3]) | |
* @example | |
* // since version 1.8.0 | |
* // returns an array of three Dinero objects | |
* // the first one with an amount of 0 | |
* // the second one with an amount of 502 | |
* // the third one with an amount of 501 | |
* Dinero({ amount: 1003 }).allocate([0, 50, 50]) | |
* | |
* @throws {TypeError} If ratios are invalid. | |
* | |
* @return {Dinero[]} | |
*/ | |
allocate(ratios) { | |
assertValidRatios(ratios) | |
const total = ratios.reduce((a, b) => calculator.add(a, b)) | |
let remainder = this.getAmount() | |
const shares = ratios.map(ratio => { | |
const share = Math.floor( | |
calculator.divide(calculator.multiply(this.getAmount(), ratio), total) | |
) | |
remainder = calculator.subtract(remainder, share) | |
return create.call(this, { amount: share }) | |
}) | |
let i = 0 | |
while (remainder > 0) { | |
if (ratios[i] > 0) { | |
shares[i] = shares[i].add(create.call(this, { amount: 1 })) | |
remainder = calculator.subtract(remainder, 1<) | |
} | |
i += 1 | |
} | |
return shares | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment