-
-
Save hijonathan/e597addcc327c9bd017c to your computer and use it in GitHub Desktop.
values = [ | |
13.626332 | |
47.989636 | |
9.596008 | |
28.788024 | |
] | |
# Round these percentage values into integers, ensuring that they equal 100% at the end. | |
roundedValues = getLargestRemainder values, 100 | |
# [14, 48, 9, 29] |
# Uses underscore.js, for brevity. | |
getLargestRemainder = (values, desiredSum) -> | |
sum = 0 | |
parts = _.map values, (item, i) -> | |
# Get rounded down integer values. | |
int = item | 0 | |
sum += int | |
return { | |
integer: int | |
decimal: item % 1 | |
# Used to return values in original order. | |
originalIndex: i | |
} | |
if sum isnt desiredSum | |
# Sort in descending order. | |
parts = _(parts).sortBy('decimal').reverse() | |
diff = desiredSum - sum | |
i = 0 | |
# Distribute additions. | |
while i < diff | |
parts[i].integer++ | |
i++ | |
return _.pluck _(parts).sortBy('originalIndex'), 'integer' | |
Hey @hijonathan, thanks for this gist!
I translated it to typescript. Will leave the code here just in case anyone needs it.
private getLargestRemainder(values: number[], desiredSum: number) {
let sum = 0;
let valueParts = values.map((value: number, index: number) => {
// Get rounded down integer values.
let integerValue = value | 0;
sum += integerValue;
return {
integer: integerValue, // Integer part of the value
decimal: value % 1, // Decimal part of the value
originalIndex: index // Used to return values in original order.
}
})
if (sum != desiredSum) {
// Sort values by decimal part
valueParts = valueParts.sort(x => x.decimal);
const diff = desiredSum - sum;
let i = 0;
// Distribute the difference.
while (i < diff) {
valueParts[i].integer++;
i++;
}
}
return valueParts.sort(x => x.originalIndex).map(p => p.integer);
}
Hey @djabif, I don't get much of coffee and your implementation really helped. Thanks!!
I just changed:
valueParts = valueParts.sort(x => x.decimal); valueParts.sort(x => x.originalIndex)
to, respectively,
valueParts = valueParts.sort((a, b) => b.decimal - a.decimal); valueParts.sort((a, b) => a.originalIndex - b.originalIndex)
as that is required to correctly sort the list by descending/ascending. Otherwise JS will just sort them alphabetically which can lead to errors.
Hey @djabif, I don't get much of coffee and your implementation really helped. Thanks!!
I just changed:
valueParts = valueParts.sort(x => x.decimal); valueParts.sort(x => x.originalIndex)to, respectively,
valueParts = valueParts.sort((a, b) => b.decimal - a.decimal); valueParts.sort((a, b) => a.originalIndex - b.originalIndex)as that is required to correctly sort the list by descending/ascending. Otherwise JS will just sort them alphabetically which can lead to errors.
Thanks for your fix and for the gist @hijonathan.
FYI the values are different on Chrom/Firefox than in Safari without this fix.
Hey @hijonathan! I found this gist very useful and am thinking of open-sourcing something where I was heavily influenced by it. Would you be okay with this? Feel free to shoot me an e-mail (it's on my GitHub profile).