Skip to content

Commit 4b7c059

Browse files
Modularize readVnNumber function instead of using classes (#43)
* Replace isNaN with Number.isNaN for clarity Replaced `isNaN` with `Number.isNaN` to improve type safety and clarity. Ensures more predictable behavior when handling `NaN` values across different types. Updated fallback logic to conform to the new check. * Simplify Vietnamese number reading logic Removed redundant classes and consolidated the Vietnamese number reading process into a single function. Streamlined implementation improves maintainability, reduces code complexity, and eliminates duplication across modules. Updated tests and adjusted configurations accordingly. * Update Vitest config to refine exclusions** Added exclusions for `tsdown.config.*` in test and coverage settings within the Vitest configuration. This ensures that unnecessary files are not included in tests or coverage reports, improving accuracy and performance. * [autofix.ci] apply automated fixes * Expand Vietnamese number reading test cases Added extensive test cases for various number patterns, including leading zeros, tens, hundreds, and boundary values. These improvements enhance the coverage and reliability of the Vietnamese number reading functionality. Updated logic to handle corner cases more accurately. * Split files * Simplify `formatNumber` logic Refactored `formatNumber` to reduce redundancy and improve readability. Combined conditions to streamline the implementation and adjusted handling of null and string inputs. Updated fallback behavior to maintain correctness and consistency. test(config): refine Vitest exclusions Added specific exclusions for `src/index.ts` and `src/format/index.ts` in both test and coverage settings. Improves test accuracy by omitting irrelevant files from coverage metrics. * Add Changeset * Add badges for publish status and metrics Added badges for publish workflow, quality gate status, and CodSpeed performance. Enhances README visualization and provides quick access to project metrics. * Split Vietnamese number reading logic Refactored number reading logic by introducing separate functions for first and subsequent groups, improving modularity and readability. Removed redundant logic and reorganized code for better maintainability. * [autofix.ci] apply automated fixes * Add tests for more number formats Added support for BigInt values in number formatting tests, ensuring compatibility across formats. Introduced comprehensive test cases for Vietnamese number reading, covering edge cases, groups, and suffix handling for enhanced accuracy and reliability. * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 239c79a commit 4b7c059

27 files changed

+1076
-721
lines changed

.changeset/late-wings-add.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
"vn-number": major
3+
---
4+
5+
## Performance Optimization & Code Quality Improvements
6+
7+
### Breaking Changes
8+
9+
- **Refactored from OOP to functional programming**: Removed class-based architecture (`NumberReader`, `Numbers`, `Thousand`, `Million`, `Billion`, `Zerofill` classes) in favor of pure functional approach
10+
- Internal implementation completely restructured for better performance and maintainability
11+
- **Note**: Public API (`readVnNumber`) remains unchanged and fully compatible
12+
13+
### Performance Improvements
14+
15+
- **Eliminated class instantiation overhead**: No more object creation for each 3-digit group
16+
- **Removed property access overhead**: Direct string index access instead of class properties
17+
- **Eliminated inheritance chain lookups**: Flat function calls instead of prototype chain traversal
18+
- **Removed dynamic dispatch**: Direct function calls instead of class lookup maps
19+
- **Optimized string operations**: More efficient concatenation patterns
20+
- **Benchmarks**: Maintained excellent performance (2.3-6.8M ops/sec for typical use cases)
21+
22+
### Code Quality
23+
24+
- **Reduced cognitive complexity**: Refactored complex functions to meet SonarCloud standards (<15 complexity)
25+
- `readThreeDigits`: Complexity reduced from 27 to <15
26+
- `readVnNumber`: Complexity reduced from 37 to <15
27+
- **Better code organization**: Split monolithic file into 5 focused modules:
28+
- `digits.ts` - Digit mapping and conversion
29+
- `utils.ts` - Utility functions for grouping and validation
30+
- `three-digits.ts` - Three-digit group reading logic
31+
- `groups.ts` - Group type calculation and processing
32+
- `index.ts` - Clean public API entry point
33+
- **Improved maintainability**: Smaller, focused functions with single responsibilities
34+
- **Better testability**: Individual modules can be tested in isolation
35+
36+
### Testing
37+
38+
- **Expanded test coverage**: Increased from 42 to 68 tests (+62% increase)
39+
- **Comprehensive edge case coverage**: Added extensive tests for:
40+
- All leading zero patterns (001, 068, 060, etc.)
41+
- All tens patterns (10-99) with special rules
42+
- All hundreds patterns (100-999)
43+
- Mixed magnitude numbers
44+
- Boundary values at each magnitude level
45+
- All Vietnamese reading rules (mốt, lăm, lẻ, không trăm)
46+
- **91 total tests passing** (68 for read module, 23 for format module)
47+
48+
### Internal Changes
49+
50+
- Removed 12 files (old class implementations and their tests)
51+
- Added 4 new modular files with clear separation of concerns
52+
- Maintained 100% backward compatibility for public API

.changeset/wide-things-dig.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"vn-number": patch
3+
---
4+
5+
Optimize logic of `formatNumber` function

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
🛠 A bunch of utility functions that work with number in 🇻🇳 Vietnamese language
44

5+
[![Publish](https://github.com/hckhanh/vn-number/actions/workflows/publish.yml/badge.svg)](https://github.com/hckhanh/vn-number/actions/workflows/publish.yml)
6+
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=hckhanh_vn-number&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=hckhanh_vn-number)
7+
[![CodSpeed Badge](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/hckhanh/vn-number?utm_source=badge)
58
[![NPM Downloads](https://img.shields.io/npm/dw/vn-number)](https://www.npmjs.com/package/vn-number)
69
[![JSR](https://jsr.io/badges/@hckhanh/vn-number/weekly-downloads)](https://jsr.io/@hckhanh/vn-number)
710

src/format/number.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ describe('formatVnNumber', () => {
66
expect(formatVnNumber(10000000)).to.eq('10.000.000')
77
})
88

9+
it('format bigint value', () => {
10+
expect(formatVnNumber(BigInt(10000000))).to.eq('10.000.000')
11+
expect(formatVnNumber(BigInt('9999999999999999'))).to.eq(
12+
'9.999.999.999.999.999',
13+
)
14+
})
15+
916
it('format invalid number by fallback value', () => {
1017
expect(formatVnNumber('100,0,0.000')).to.eq('0')
1118
})
@@ -36,6 +43,13 @@ describe('formatVnCurrency', () => {
3643
expect(formatVnCurrency(10000000)).to.match(/10\.000\.000\s/)
3744
})
3845

46+
it('format bigint value in VND', () => {
47+
expect(formatVnCurrency(BigInt(10000000))).to.match(/10\.000\.000\s/)
48+
expect(formatVnCurrency(BigInt('9999999999999999'))).to.match(
49+
/9\.999\.999\.999\.999\.999\s/,
50+
)
51+
})
52+
3953
it('format invalid number by fallback value', () => {
4054
expect(formatVnCurrency('100,0,0.000')).to.eq('0\u00A0₫')
4155
})
@@ -73,6 +87,11 @@ describe('formatVnPercent', () => {
7387
expect(formatVnPercent(0.1)).to.eq('10%')
7488
})
7589

90+
it('format bigint value as percent', () => {
91+
expect(formatVnPercent(BigInt(1))).to.eq('100%')
92+
expect(formatVnPercent(BigInt(5))).to.eq('500%')
93+
})
94+
7695
it('format invalid number by fallback value', () => {
7796
expect(formatVnPercent('100,0,0.000')).to.eq('0%')
7897
})

src/format/number.ts

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,13 @@ function formatNumber(
1010
formatter: Intl.NumberFormat,
1111
fallbackValue: string,
1212
) {
13-
if (
14-
(typeof number === 'number' && !isNaN(number)) ||
15-
typeof number === 'bigint'
16-
) {
17-
return formatter.format(number)
18-
} else if (typeof number === 'string') {
19-
try {
20-
const num = Number(number)
21-
return isNaN(num) ? fallbackValue : formatter.format(num)
22-
} catch {
23-
return fallbackValue
24-
}
25-
} else {
26-
return fallbackValue
27-
}
13+
if (number == null) return fallbackValue
14+
if (typeof number === 'bigint') return formatter.format(number)
15+
if (typeof number === 'number')
16+
return Number.isNaN(number) ? fallbackValue : formatter.format(number)
17+
18+
const num = Number(number)
19+
return Number.isNaN(num) ? fallbackValue : formatter.format(num)
2820
}
2921

3022
const VN_NUMBER_FORMATTER = new Intl.NumberFormat('vi-VN')

src/read/Billion.test.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/read/Billion.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/read/Million.test.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/read/Million.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/read/Number.test.ts

Lines changed: 0 additions & 154 deletions
This file was deleted.

0 commit comments

Comments
 (0)