问题描述
我正在编写一个有两个调整轴的自定义图表。 我想向轴显示四舍五入的数字,例如
输入 | 输出 |
---|---|
[950,2050,3000,4500] |
["1K","2K","3K","5K"] |
问题在于数字可能非常接近:
输入 | 输出 |
---|---|
[950,1100,1200,1300] |
["1K","1K","1K"] |
当然,这是天真的方法,而且很糟糕。
我想要类似的东西:
输入 | 输出 |
---|---|
[950,1300] |
["950","1.1K","1.2K","1.3K"] |
有没有解决上述问题的库?
编辑:
这是我的解决方案(在应有的地方加热后作为问题的一部分添加):
我不知道它是否是最佳的,或者它是否会因某些测试用例而中断。你能找到更好的解决方案吗(比如更少的行,更快,更简洁?)
const numberFormat = n => n.toLocaleString(
"en-US",{ minimumFractionDigits: 0 }
);
const SUFFIXES = ["","K","M","B","T"];
const getPrevSuffix = (suffix) => {
return SUFFIXES[SUFFIXES.indexOf(suffix) - 1] || "";
};
const getSuffix = (value) => {
const naive = numberFormat(excelRoundPositive(value,-digits(value)));
const thousands = naive.match(/,.../g)?.length;
const postfix = SUFFIXES[thousands || 0] ?? "T";
return postfix;
};
const pows = {
undefined: 1,null: 1,"": 1,K: 1000,k: 1000,M: 1000000,m: 1000000,B: 1000000000,b: 1000000000,T: 1000000000000,t: 1000000000000
};
export const excelRound = (value,power = 1) => {
const pow = Math.pow(10,power);
return Math.round(value * pow) / pow;
};
export const excelRoundPositive = (value,power) => {
if (value === 0) return 0;
if (value < 0) return -excelRoundPositive(-value,power);
let pow = power;
while (excelRound(value,pow) === 0) {
pow += 1;
}
return excelRound(value,pow);
};
const digits = (value) => {
if (value !== 0) return Math.floor(Math.log10(Math.abs(value)));
return 0;
};
/**
* Zero gets formatted as `0`
* Negative numbers get formatted as positives,with a minus sign
* Numbers get formatted with X decimals,where X = max(decimal of suffix)
* is this dYnAmIc ProGrAMmInG
*/
export const formatInContext = (value,context) => {
const suffix = getSuffix(value);
const ctxNoZeros = context.filter((a) => a);
const formatted = ctxNoZeros.map((v) =>
formatInContextGreedy(v,ctxNoZeros)
);
const maxDecimals = Math.max(
...formatted.filter(([d,s]) => s === suffix).map(([d,s]) => d)
);
const rounded = excelRoundPositive(value / pows[suffix],maxDecimals);
if (Math.abs(rounded) >= 1)
return `${excelRoundPositive(value / pows[suffix],maxDecimals)}${suffix}`;
return `${1000 * rounded}${getPrevSuffix(suffix)}`;
};
const formatInContextGreedy = (
value,context = [],decimals = -Math.min(2,digits(value)) // This is,round up to hundreds,if value has enough digits
) => {
const suffix = getSuffix(value);
const currentRounding = excelRoundPositive(value / pows[suffix],decimals);
const isAnyWithSameRounding = context
.filter((x) => x !== value && getSuffix(x) === suffix)
.some(
(v) => excelRoundPositive(v / pows[suffix],decimals) === currentRounding
);
if (!isAnyWithSameRounding) return [decimals,suffix];
return formatInContextGreedy(value,context,decimals + 1);
};
describe("Format Ticks Number That Should Not collide",() => {
test.each`
value | context | result
${0} | ${[0,101,1428,1904,2380,2856]} | ${"0"}
${476} | ${[476,952,2856]} | ${"500"}
${10} | ${[10,2856]} | ${"10"}
${1001} | ${[0,1000,1001,1234,2000,2100]} | ${"1.001K"}
${1234} | ${[0,2100]} | ${"1.234K"}
${1000} | ${[0,2100]} | ${"1K"}
${2000} | ${[0,2100]} | ${"2K"}
${2100} | ${[0,2100]} | ${"2.1K"}
${27.3} | ${[27.3,29,30.3,31.1]} | ${"27"}
${2100000000000000} | ${[2100000000000000]} | ${"2100T"}
${-1} | ${[-1]} | ${"-1"}
${950} | ${[950,1300]} | ${"1K"}
${950} | ${[950,1300]} | ${"950"}
${959} | ${[950,1040,1300]} | ${"960"}
${951} | ${[900,920,950,951,990]} | ${"951"}
${841} | ${[841,990]} | ${"800"}
${951} | ${[841,990]} | ${"950"}
${990} | ${[841,990]} | ${"990"}
${950} | ${[950,1300]} | ${"950"}
${274} | ${[274,548,822,1105]} | ${"300"}
${274} | ${[0,274,1096,1370]} | ${"300"}
${1152} | ${[0,384,768,1152]} | ${"1K"}
${1152} | ${[384,1152]} | ${"1K"}
`(
"Convert $value when found in $context to $result",({ value,result }) => {
expect(formatInContext(value,context)).toBe(result);
}
);
});
谢谢!
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)