Skip to content

Instantly share code, notes, and snippets.

@scwood
Last active December 14, 2021 14:07
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save scwood/e58380174bd5a94174c9f08ac921994f to your computer and use it in GitHub Desktop.
Save scwood/e58380174bd5a94174c9f08ac921994f to your computer and use it in GitHub Desktop.
Largest remainder round
/**
* largestRemainderRound will round each number in an array to the nearest
* integer but make sure that the the sum of all the numbers still equals
* desiredTotal. Uses largest remainder method. Returns numbers in order they
* came.
*
* @param {number[]} numbers - numbers to round
* @param {number} desiredTotal - total that sum of the return list must equal
* @return {number[]} the list of rounded numbers
* @example
*
* var numbers = [13.6263, 47.9896, 9.5960, 28.7880];
* largestRemainderRound(numbers, 100);
*
* // => [14, 48, 9, 29]
*
*/
function largestRemainderRound(numbers, desiredTotal) {
var result = numbers.map(function(number, index) {
return {
floor: Math.floor(number),
remainder: getRemainder(number),
index: index,
};
}).sort(function(a, b) {
return b.remainder - a.remainder;
});
var lowerSum = result.reduce(function(sum, current) {
return sum + current.floor;
}, 0);
var delta = desiredTotal - lowerSum;
for (var i = 0; i < delta; i++) {
result[i].floor++;
}
return result.sort(function(a, b) {
return a.index - b.index;
}).map(function(result) {
return result.floor;
});
}
function getRemainder(number) {
var remainder = number - Math.floor(number);
return remainder.toFixed(4);
}
@Jhegs
Copy link

Jhegs commented May 4, 2018

Thank you, really helpful. :)

@anantl05
Copy link

anantl05 commented Aug 6, 2020

Really Helpful..

@yUtopist
Copy link

yUtopist commented Mar 27, 2021

Thank you!
The only thing is - i wouldnt use reminder.toFixed(4) since it returns a formatted string. instead i woul use Math.round(reminder * 1000)/1000

here is a bit modified code, now you can pass whole numbers and denominator and you will get output of rounded divided numbers by denominator

const largestRemainderRound = (numbers, desiredTotal) => {
  const _upperSum = numbers.reduce((a, b) => a + b)
  const _sortedArray = numbers.map((number, index) => {
    const _value = (number / _upperSum) * desiredTotal
    return {
      floor: Math.floor(_value),
      remainder: Math.round((_value - Math.floor(_value)) * 10000) / 10000,
      index: index
    }
  }).sort((a, b) => b.remainder - a.remainder)

  const _lowerSum = _sortedArray.reduce((a, b) => a + b.floor, 0)
  for (let i = 0; i < desiredTotal - _lowerSum; i++) {
    _sortedArray[i].floor++;
  }

  return _sortedArray.sort((a, b) => a.index - b.index).map(e => e.floor)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment