Wenzi

数字转换为更高量级单位的工具方法

蚊子前端博客
发布于 2024/06/05 10:08
我们日常开发中需要把较大的数字转为更高的量级,一起来看下吧

在我们日常开发中,经常会遇到将一些具体数字提升转换为最高单位的做法。如阅读量多万时则以「万」为单位,并保留 2 位小数,过亿则以「亿」为单位等;还有文件大小超过1MB后则以「MB」为单位,超过 1GB 后则「GB」为单位等。

就是当数据比较大,达到该领域的某常用单位时,我们需要将数据转为最高量级的单位。一方面是缩短数据的长度,再一个是对用户更加的直观。当数据太长时,用户就会对最后位置的数字不太敏感,更加关注当前数字处在一个什么样的量级。

我们以阅读量的数字转换为例,这个工具方法我们主要实现的要点有:

  1. 若数字在 1 万以内,则原样返回;
  2. 小数点后有 0 结尾时,省去该位置,如我们保留 1 位小数时得到的数字是「12.0 万」时,最终会展示为「12 万」;
  3. 若数字较大时,会一直提升到设定的最大量级,如过万则以万为单位,若过亿则以亿为单位;

分享下当数字达到某量级时转为该对应单位的工具方法:

/**
 * 将数字提升到最高的量级
 * @param num 要转换的数字
 * @param config 配置
 * @returns 返回转换后的数字
 */
const covertNumToMaxSize = (
  num: number,
  config?: {
    decimal?: number; // 要保留几位小数,默认保留1位
    strict?: boolean; // 是否严格保留小数位数,默认会舍弃小数点后面的0
    unit?: boolean; // 是否需要单位,有的场景可能只是比大小,不需要单位,默认有单位
    thousandth?: boolean; // 是否要千分位,只在万级以下的数字才会生效,默认true
    sizes?: [number, string][]; // 要转换的基数和单位
  }
) => {
  if (typeof num !== "number") {
    // 万一接口在数据为0时返回null或undefined等类型
    return 0;
  }

  const decimal = config?.decimal ?? 1;
  const strict = config?.strict ?? false;
  const unit = config?.unit ?? true;
  const thousandth = config?.thousandth ?? true;
  const sizes = config?.sizes || [
    [0, ""],
    [1e4, "万"],
    [1e8, "亿"],
    [1e12, "兆"],
  ];

  let i = 0;

  // 判断数字处于哪个量级
  for (; i < sizes.length - 1; i++) {
    if (num < sizes[i + 1][0]) {
      break;
    }
  }
  if (sizes[i][0]) {
    num /= sizes[i][0];
  }

  let result: number | string = num.toFixed(decimal);
  if (!strict) {
    // 非严格要求小数位数,则舍弃小数最后的0
    result = Number(result);
  }
  if (thousandth) {
    const [first, last] = String(result).split(".");

    if (first.length > 3 || (last?.length || 0) > 3) {
      // 将字符串添加千分位
      result = String(result).replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, "$1,");
    }
  }
  if (unit && sizes[i][1]) {
    result += sizes[i][1];
  }
  return result;
};

我们在实现了数据的量级提升功能后,还加了一些配置的字段,方便灵活地返回我们需要的数据:

  1. 可以配置最多保留几位小数,默认保留 1 位;
  2. 是否保留小数点后的后置 0,默认是 false,会舍弃;
  3. 是否需要对应量级的单位,默认携带,可以配置为只返回数据;
  4. 是否需要转为千分位,默认转换;
  5. 可以自定义量级的基数和单位;

我们来测试几个数据,下面的测试用例全部通过:

import covertNumToMaxSize from "./index";

describe("test covertNumToMaxSize", () => {
  test("should get zero when param is not number", () => {
    expect(covertNumToMaxSize(null as any)).toBe(0);
    expect(covertNumToMaxSize("234" as any)).toBe(0);
  });
  test("should return original num when not greater", () => {
    expect(covertNumToMaxSize(123)).toBe(123);
  });
  test("should one decimal", () => {
    expect(covertNumToMaxSize(1234567)).toBe("123.5万");
  });
  test("should give up last decimal zero", () => {
    expect(covertNumToMaxSize(10345)).toBe("1万");
  });
  test("should get max size", () => {
    expect(covertNumToMaxSize(1367892445)).toBe("13.7亿");
  });
  test("should get two decimal when last num is not zero", () => {
    expect(covertNumToMaxSize(1234567, { decimal: 2 })).toBe("123.46万");
  });
  test("should get one decimal when last num is zero although config decimal is 2", () => {
    expect(covertNumToMaxSize(1234017, { decimal: 2 })).toBe("123.4万");
  });
  test("should get two decimal although last num is zero when strict is true", () => {
    expect(covertNumToMaxSize(1234017, { decimal: 2, strict: true })).toBe("123.40万");
  });
  test("should get thousandth", () => {
    // 千分位
    expect(covertNumToMaxSize(1234)).toBe("1,234");
  });
  test("should not get thousandth when config.thousandth is false", () => {
    // 千分位
    expect(covertNumToMaxSize(1234, { thousandth: false })).toBe(1234);
  });
  test("should not get unit when config.unit is false", () => {
    expect(covertNumToMaxSize(1234017, { unit: false })).toBe(123.4);
  });
  test("diy sizes", () => {
    expect(
      covertNumToMaxSize(1234017, {
        sizes: [
          [0, ""],
          [1024, "KB"],
          [1024 * 1024, "MB"],
        ],
      })
    ).toBe("1.2MB");
  });
});

我们在测试了几种不同长度的数据后,均能满足我们的要求。

单测报告

在文件大小的场景中转换单位时,原理是一样的。我们可以自定义配置中的sizes,只不过基数变成了1024,单位也从「万亿」变成了「MB」、「GB」等。

标签:fe
阅读(326)
Simple Empty
No data