Skip to content

Commit

Permalink
refactor: formatNumberWithUnits 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
ssi02014 committed Jan 12, 2025
1 parent 31e5a6e commit 19a34d9
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
type FloorUnit =
| 1
| 10
| 100
| 1_000
| 10_000
| 100_000
| 1_000_000
| 10_000_000
| 100_000_000
| 1_000_000_000
| 10_000_000_000
| 100_000_000_000
| 1_000_000_000_000;

export interface Unit {
unit: string;
value: number;
}

export interface FormatNumberWithUnitsOptions {
units?: Unit[] | readonly Unit[];
commas?: boolean;
floorUnit?: FloorUnit;
space?: boolean;
decimal?: number;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { formatNumberWithCommas } from '../../formatter/formatNumberWithCommas';
import {
FormatNumberWithUnitsOptions,
Unit,
} from './formatNumberWithUnits.types';

/**
* @description 쉼표 사용 여부에 따라 숫자를 포맷팅하는 함수
Expand All @@ -7,9 +11,68 @@ import { formatNumberWithCommas } from '../../formatter/formatNumberWithCommas';
* @param {boolean} commas - 쉼표 사용 여부
* @returns {string} 포맷팅된 문자열
*/
export const getNumberWithConditionalCommas = (
const getNumberWithConditionalCommas = (
value: number | string,
commas: boolean
): string => {
return commas ? formatNumberWithCommas(value) : String(value);
};

/**
* @description 주어진 단위(units)에 따라 숫자를 포맷팅하는 함수
*
* @param {number} value - 포맷팅할 숫자 값
* @param {Unit[] | readonly Unit[]} units - 변환할 단위 배열
* @param {Omit<Required<FormatNumberWithUnitsOptions>, 'units'>} options - 포맷팅 옵션
* @param {boolean} options.commas - 천 단위 구분 쉼표 사용 여부입니다.
* @param {boolean} options.space - 단위 사이 공백 추가 여부입니다.
* @param {number} options.decimal - 소수점 자릿수입니다.
* @returns {string} 포맷팅된 문자열
*/
export const getFormattedValueWithUnits = (
value: number,
units: Unit[] | readonly Unit[],
options: Omit<Required<FormatNumberWithUnitsOptions>, 'units'>
): string => {
const { commas, space, decimal, floorUnit } = options;

const absoluteValue = Math.abs(value);
const isNegative = value < 0;

// value가 floorUnit(버림 단위)보다 작으면 '0'을 반환
if (absoluteValue < floorUnit) {
return '0';
}

let formattedResult = '';
let remainingValue =
floorUnit > 1
? Math.floor(absoluteValue / floorUnit) * floorUnit
: absoluteValue;

// unit 별로 나누기
for (let i = 0; i < units.length; i++) {
const { unit, value } = units[i];
const quotient = Math.floor(remainingValue / value);

if (quotient <= 0) continue;

formattedResult += `${getNumberWithConditionalCommas(
quotient,
commas
)}${unit}${space ? ' ' : ''}`;

remainingValue %= value;
}

// 남은 remainingValue가 있으면 추가
if (remainingValue > 0) {
formattedResult += `${getNumberWithConditionalCommas(
remainingValue.toFixed(decimal),
commas
)}`;
}

// 음수일 경우 앞에 '-' 붙이며, 앞/뒤 공백 제거
return (isNegative ? '-' : '') + formattedResult.trim();
};
77 changes: 10 additions & 67 deletions packages/utils/src/formatter/formatNumberWithUnits/index.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,6 @@
import { getNumberWithConditionalCommas } from './formatNumberWithUnits.utils';
import { getFormattedValueWithUnits } from './formatNumberWithUnits.utils';
import { isNumber } from '../../validator/isNumber';

interface Unit {
unit: string;
value: number;
}

type FloorUnit =
| 1
| 10
| 100
| 1_000
| 10_000
| 100_000
| 1_000_000
| 10_000_000
| 100_000_000
| 1_000_000_000
| 10_000_000_000
| 100_000_000_000
| 1_000_000_000_000;

interface FormatNumberWithUnitsOptions {
units?: Unit[] | readonly Unit[];
commas?: boolean;
floorUnit?: FloorUnit;
space?: boolean;
decimal?: number;
}
import { FormatNumberWithUnitsOptions } from './formatNumberWithUnits.types';

/**
* @description `숫자` 혹은 `숫자로 이루어진 문자열`을 주어진 `단위` 별로 포맷팅하는 함수입니다.
Expand Down Expand Up @@ -95,9 +68,6 @@ export function formatNumberWithUnits(
const sortedUnits = [...units].sort((a, b) => b.value - a.value);

const valueToUse = isNumber(value) ? value : Number(value);
const isNegative = valueToUse < 0;
const absoluteValue = Math.abs(valueToUse);

// 에러 처리
if (isNaN(valueToUse)) {
throw new Error('value는 숫자 혹은 숫자로 이뤄진 문자열이여야 합니다.');
Expand All @@ -111,44 +81,17 @@ export function formatNumberWithUnits(
if (isValidFloorUnit) {
throw new Error('floorUnit은 1을 포함한 10의 제곱수여야 합니다.');
}

if (!Number.isInteger(decimal) || decimal < 0) {
throw new Error('decimal은 0 이상의 정수여야 합니다.');
}

// value가 floorUnit(버림 단위)보다 작으면 '0'을 반환
if (absoluteValue < floorUnit) {
return '0';
}

let formattedResult = '';
let remainingValue =
floorUnit > 1
? Math.floor(absoluteValue / floorUnit) * floorUnit
: absoluteValue;

// unit 별로 나누기
for (let i = 0; i < sortedUnits.length; i++) {
const { unit, value } = sortedUnits[i];
const quotient = Math.floor(remainingValue / value);

if (quotient <= 0) continue;

formattedResult += `${getNumberWithConditionalCommas(
quotient,
commas
)}${unit}${space ? ' ' : ''}`;

remainingValue %= value;
}

// 남은 remainingValue가 있으면 추가
if (remainingValue > 0) {
formattedResult += `${getNumberWithConditionalCommas(
remainingValue.toFixed(decimal),
commas
)}`;
}
const formattedResult = getFormattedValueWithUnits(valueToUse, sortedUnits, {
commas,
space,
decimal,
floorUnit,
});

// 음수일 경우 앞에 '-' 붙이며, 앞/뒤 공백 제거
return (isNegative ? '-' : '') + formattedResult.trim();
return formattedResult;
}

0 comments on commit 19a34d9

Please sign in to comment.