Skip to content

Instantly share code, notes, and snippets.

@jerolan
Created February 29, 2024 18:24
Show Gist options
  • Save jerolan/c018e19210806be35fa6cab5c2240cd1 to your computer and use it in GitHub Desktop.
Save jerolan/c018e19210806be35fa6cab5c2240cd1 to your computer and use it in GitHub Desktop.
/**
* 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