diff --git a/README.md b/README.md index 6487091..a3527b0 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ No specific version of nodejs and npm is required, but for the usage as an npm p - [Common Utilities](#common-utils) - [removeByKey](#remove-by-key) - [removeDeepByKey](#remove-deep-by-key) + - [beautifyNumber](#beautify-number) - [List Utils](#list-utils) - [findByPrimaryKey](#find-by-primary-key) - [Contributing](#contributing) @@ -212,6 +213,42 @@ const result = removeDeepByKey(['c'], target); -> { a: 'a', b: 'B' } const targetEqualsResult = result === target; -> true ``` +#### beautifyNumber() + +```ts +beautifyNumber(value: number, options: IBeautifyNumberOptions); +``` + +Produces a formatted string that was created out of the number provided as an input of the function. +Formatting rules: + +- splits a whole part of a number by thousands +- adds a `joint` symbol between thousands +- adds a `delimiter` symbol between a whole and a floating part + +#### Options + +| Name | Type | Required | Default value | +| ------- | ------------------------ | -------- | -------------------------------- | +| value | `number` | + | - | +| options | `IBeautifyNumberOptions` | - | `{ delimiter: '.', joint: ' ' }` | + +Examples: + +```ts +// Whole numbers +const result = beautifyNumber(1000); -> '1 000' +const result = beautifyNumber(1_000); -> '1 000' + +// With floating point +const result = beautifyNumber(1000.123); -> '1 000.123' +const result = beautifyNumber(1000.123, { delimiter: ',' }); -> '1 000,123' +const result = beautifyNumber(1000.123, { delimiter: ',', joint: '_' }); -> '1_000,123' + +// Throws error +const result = beautifyNumber(1000.123, { delimiter: ',', joint: ',' }); -> [Error: Please provide different delimiter and joint values] +``` + ### List Utils A common function for initializing a bag of useful traversing utilities for going over, finding and mutating nodes within some nested objects structures. @@ -228,6 +265,7 @@ Might be useful if you want to create a set of handy functions at one place, bin | initOptions | `IInitListUtilsOptions` | + | - | ##### `IInitListUtilsOptions` + | Property | Type | Default value | | ----------- | -------- | ------------- | | primaryKey | `string` | - | diff --git a/package.json b/package.json index 17c48a6..5deda18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "handilities", - "version": "0.0.6", + "version": "0.0.7", "description": "Handy utilities for JS, TS, React in browser and server env", "main": "lib", "scripts": { diff --git a/src/common/__tests__/beautifyNumber.test.ts b/src/common/__tests__/beautifyNumber.test.ts new file mode 100644 index 0000000..d877bab --- /dev/null +++ b/src/common/__tests__/beautifyNumber.test.ts @@ -0,0 +1,77 @@ +import { sameJointAndDelimiterErrorMessage, beautifyNumber } from "../beautifyNumber"; + +describe("beautifyNumber", () => { + describe("beautifyNumber with no options passed", () => { + test.each([ + [1, "1"], + [100, "100"], + [1000, "1 000"], + [100000, "100 000"], + [123456789, "123 456 789"], + ])('converts "%s" into "%s"', (value, result) => { + expect(beautifyNumber(value)).toBe(result); + }); + }); + + describe('beautifyNumber with the "," joint', () => { + test.each([ + [1, "1"], + [100, "100"], + [1000, "1,000"], + [100000, "100,000"], + [123456789, "123,456,789"], + ])('converts "%s" into "%s"', (value, result) => { + expect(beautifyNumber(value, { joint: "," })).toBe(result); + }); + }); + + describe("beautifyNumber for floating numbers", () => { + test.each([ + [1.12, "1.12"], + [100.123, "100.123"], + [1000.1234, "1 000.1234"], + [100000.12345, "100 000.12345"], + [123456789.0123456, "123 456 789.0123456"], + ])('converts "%s" into "%s"', (value, result) => { + expect(beautifyNumber(value)).toBe(result); + }); + }); + + describe('beautifyNumber for floating numbers with the "_" joint, and "," delimiter', () => { + test.each([ + [1.12, "1,12"], + [100.123, "100,123"], + [1000.1234, "1_000,1234"], + [100000.12345, "100_000,12345"], + [123456789.0123456, "123_456_789,0123456"], + ])('converts "%s" into "%s"', (value, result) => { + expect(beautifyNumber(value, { joint: "_", delimiter: "," })).toBe(result); + }); + }); + + describe("beautifyNumber for negative floating numbers", () => { + test.each([ + [-1.12, "-1.12"], + [-100.123, "-100.123"], + [-1000.1234, "-1 000.1234"], + [-100000.12345, "-100 000.12345"], + [-123456789.0123456, "-123 456 789.0123456"], + ])('converts "%s" into "%s"', (value, result) => { + expect(beautifyNumber(value)).toBe(result); + }); + }); + + test('works for "_" number notation', () => { + expect(beautifyNumber(12_001_123)).toBe("12 001 123"); + }); + + test("throws the error if joint and delimiter are equal", () => { + try { + beautifyNumber(12_001_123, { joint: "", delimiter: "" }); + } catch (error) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(error.message).toContain(sameJointAndDelimiterErrorMessage); + } + }); +}); diff --git a/src/common/beautifyNumber.ts b/src/common/beautifyNumber.ts new file mode 100644 index 0000000..9278074 --- /dev/null +++ b/src/common/beautifyNumber.ts @@ -0,0 +1,37 @@ +interface IBeautifyNumberOptions { + delimiter?: string; + joint?: string; +} + +interface IBeautifyNumber { + (value: number, options?: IBeautifyNumberOptions): string; +} + +const sameJointAndDelimiterErrorMessage = "Please provide different delimiter and joint values"; + +const DEFAULT_DELIMITER = "."; +const DEFAULT_JOINT = " "; + +/** + * + * @param value number to format based on options + * @param options "joint" symbol to add between thousands, and "delimiter" symbol to join whole and floating parts + * @returns string produced out of a formatted number + */ +const beautifyNumber: IBeautifyNumber = (value, options = {}) => { + const { delimiter = DEFAULT_DELIMITER, joint = DEFAULT_JOINT } = options; + + if (delimiter === joint) { + const error = new Error(sameJointAndDelimiterErrorMessage); + + throw error; + } + + const [whole, decimal] = String(value).split("."); + const wholeWithJoint = whole.replace(/\B(?=(\d{3})+(?!\d))/g, joint); + + return decimal ? `${wholeWithJoint}${delimiter}${decimal}` : wholeWithJoint; +}; + +export type { IBeautifyNumber, IBeautifyNumberOptions }; +export { sameJointAndDelimiterErrorMessage, beautifyNumber }; diff --git a/src/common/index.ts b/src/common/index.ts index eb9dc28..4ef6202 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -6,3 +6,6 @@ export type { IRemoveByKey } from "./removeByKey"; export { initListUtils, findByPrimaryKey } from "./list-utils"; export type { IInitListUtilsOptions, IFindByPrimaryKeyMutation } from "./list-utils"; + +export { beautifyNumber } from "./beautifyNumber"; +export type { IBeautifyNumber, IBeautifyNumberOptions } from "./beautifyNumber";