Skip to content

Instantly share code, notes, and snippets.

@drzhbe
Created September 10, 2018 01:01
Show Gist options
  • Save drzhbe/2f7083d79bedbf8fbd4d51bc53ad1c42 to your computer and use it in GitHub Desktop.
Save drzhbe/2f7083d79bedbf8fbd4d51bc53ad1c42 to your computer and use it in GitHub Desktop.
Largest remainder method of fixing 101%
/** Scales values to be a percentage of their sum. E.g., [20, 30] -> [40, 60] */
function scaleToPercentages(rows: Row[]): Row[] {
rows.forEach(([_, vs]) => Preconditions.checkArgument(vs.length === 1)); // Only supports one
// column.
if (rows.length === 0) {
return rows;
}
const sum = rows
.map(([_, [v]]) => v)
.reduce((acc, v) => acc + v, 0);
const percents: Row[] = rows.map(([l, vs]) => ([
l,
vs.map(v => (v / sum) * 100),
] as Row));
// According to the "Largest remainder method" (to avoid "101%" case):
// 1. Take floored percentage values
// 2. Calc remaining percents
// 3. Distribute them between the largest remainder values
let remainingPercents = 100;
const remainders: [number, number][] = []; // [index, remainder][]
for (let i = 0; i < percents.length; i++) {
const [, [decimal]] = percents[i];
const integer = Math.floor(+decimal);
remainingPercents -= integer;
remainders[i] = [i, +decimal - integer];
percents[i][1] = [integer];
}
// Distribute remaining percents between the highest remainder values.
remainders.sort((a, b) => a[1] === b[1]
? a[0] - b[0] // save position if values are equal by comparing indexes
: b[1] - a[1]);
for (let i = 0; i < remainingPercents; i++) {
const [index] = remainders[i];
const [, [value]] = percents[index];
percents[index][1] = [+value + 1];
}
return percents;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment