示例
[3, 3, 8] => ["21%", "21%", "58%"]
[{label: "test1",value: 2}, {label: 'tes2', value: 4}] => [{label: "test1",value: 2, percentValue: '33%'}, {label: 'tes2', value: 4, percentValue: '67%'}]
场景
画百分比图表时,需要将原始值转化为相应百分比形式展示。
关键问题
如何保证百分比总和为100%?
比如: [3, 3, 8]直接使用toFixed四舍五入取整 => [21, 21, 57],和为99,不是100;
解决方案:使用最大余额法进行进位确保百分比和为100%;
核心源码:
while (currentSum < targetSeats) {
let max = Number.NEGATIVE_INFINITY;
let maxId = null;
for (let i = 0, len = remainder.length; i < len; ++i) {
if (remainder[i] > max) {
max = remainder[i];
maxId = i;
}
}
++seats[maxId];
remainder[maxId] = 0;
++currentSum;
}
链接:juejin.cn/post/694644… // 不是本文的源码
当出现[3,3,3]情况,上述方法将直接进位为[34%,33%,33%],未保证相同值均分
比如:[3,3,3] 应=> [33%,33%,33%] // 不进位
解决方案:
// 对余数出现次数进行计数
const repeatRemainders: Record<string, K> = remainders.reduce(
(pre, cur, idx) => {
pre[cur] = {
time: (pre[cur]?.time || 0) + 1,
idxArr: (pre[cur]?.idxArr || []).concat(idx),
value: cur
};
return pre;
},
{}
);
// 降序排序
const sortedRepeatRemainders: K[] = sortArray(
Object.values(repeatRemainders),
{
field: 'value',
order: 'desc',
}
);
let currentSum: number = seats.reduce((pre, cur) => pre + cur);
sortedRepeatRemainders.forEach((item) => {
// 保证相同值同加同减
if (currentSum + item.time <= targetSeats) {
item.idxArr.forEach((idx) => seats[idx]++);
currentSum += item.time;
}
});