@norbulcz/num-parse
v0.1.0
Published
Strict, locale-tolerant number parser (US/EU/CH) with validated grouping & currency stripping. Zero deps.
Maintainers
Readme
@norbulcz/num-parse
Strict, locale-tolerant number parser (US/EU/CH) with validated grouping. Zero dependencies.
A robust TypeScript library for parsing numbers from strings with support for multiple international formats including US, European, and Swiss number formatting conventions.
Features
- 🌍 Multi-locale support: US, EU, and Swiss number formats
- 🔒 Strict validation: Validates proper thousands separators and decimal points
- 🚫 Zero dependencies: Lightweight with no external dependencies
- 📦 TypeScript: Full TypeScript support with type definitions
- 🧪 Well tested: Comprehensive test suite with 300+ test cases
- 📱 Modern: ES modules and CommonJS support
- ⚡ High Performance: 4.4M+ parses per second
Why Choose @norbulcz/num-parse?
- Smaller (1.2KB gzipped, zero deps)
- Stricter (rejects invalid groupings)
- Locale-flexible (US, EU, Swiss, plain, space grouping, with currency symbols)
- Safer (never silently misparses into wrong numbers)
- Simple (no Intl dependency, no polyfills, no moment.js or numeral.js — just a single function)
- Predictable (invalid input → returns null, never throws)
Performance
The library is highly optimized for performance:
- 4.4M+ parses per second on modern hardware
- 0.0002ms average parse time per operation
- Pre-compiled regex patterns for maximum efficiency
- Zero dependencies for minimal bundle impact
Bundle Size
- ESM: ~1.2KB gzipped
- CJS: ~1.6KB gzipped
- Types: ~1.4KB
Installation
npm install @norbulcz/num-parseQuickstart
import { parseNumber } from '@norbulcz/num-parse';
// US format
parseNumber("1,234.56"); // → 1234.56
// European format
parseNumber("1.234,56"); // → 1234.56
// Swiss format (both apostrophe types)
parseNumber("1'234.56"); // → 1234.56
parseNumber("1'234.56"); // → 1234.56 (curly apostrophe)
// Currency symbols (automatically removed)
parseNumber("₪1,234.56"); // → 1234.56
parseNumber("1,234.56$"); // → 1234.56
// Plain numbers (no separators)
parseNumber("1234.56"); // → 1234.56
parseNumber("1234"); // → 1234
// Space grouping
parseNumber("1 234.56"); // → 1234.56
parseNumber("1 234 567.89"); // → 1234567.89Usage
import { parseNumber } from '@norbulcz/num-parse';
// US format
parseNumber('1,234.56'); // → 1234.56
parseNumber('12,345.67'); // → 12345.67
// European format
parseNumber('1.234,56'); // → 1234.56
parseNumber('12.345,67'); // → 12345.67
// Swiss format (apostrophe as thousands separator)
parseNumber("1'234.56"); // → 1234.56
parseNumber("1'234,56"); // → 1234.56
// Negative numbers
parseNumber('-1,234.56'); // → -1234.56
parseNumber('-1.234,56'); // → -1234.56
// Edge cases
parseNumber('.5'); // → 0.5
parseNumber(',5'); // → 0.5
parseNumber('1,000'); // → 1000
parseNumber('1.000'); // → 1000
// Currency symbols (automatically removed)
parseNumber('₪1,234.56'); // → 1234.56 (Israeli Shekel)
parseNumber('₿0.001'); // → 0.001 (Bitcoin)
parseNumber('₩1,234.56'); // → 1234.56 (Korean Won)
parseNumber('₹1,234.56'); // → 1234.56 (Indian Rupee)
parseNumber('1,234.56$'); // → 1234.56 (US Dollar at end)
parseNumber('1.234,56€'); // → 1234.56 (Euro at end)
// Scientific notation (disabled by default)
parseNumber('1.5e3'); // → null (disabled by default)
parseNumber('1.5e3', { allowScientific: true }); // → 1500
// Plain inputs & spaces
parseNumber('3333'); // → 3333
parseNumber('12345.67'); // → 12345.67
parseNumber('12345,67'); // → 12345.67
parseNumber('1 123.34'); // → 1123.34
parseNumber('2 276,00'); // → 2276 (NBSP)
// Invalid inputs return null
parseNumber('invalid'); // → null
parseNumber('1,23,456'); // → null (invalid grouping)
parseNumber("12'34"); // → null (invalid apostrophe grouping)
parseNumber('123.'); // → null (trailing decimal)
parseNumber(''); // → nullSupported Formats
US Format
- Thousands separator: comma (
,) - Decimal separator: period (
.) - Examples:
1,234.56,12,345.67
European Format
- Thousands separator: period (
.) - Decimal separator: comma (
,) - Examples:
1.234,56,12.345,67
Swiss Format
- Thousands separator: apostrophe (
') - Decimal separator: period (
.) or comma (,) - Examples:
1'234.56,1'234,56
Currency Symbol Support
- Automatically removes all Unicode currency symbols using the
\p{Sc}class - Supports:
$,€,£,¥,₹,₽,¢,₪,₿,₩,₫,₴,₦,₵, and many more - Examples:
₪1,234.56→1234.56,₿0.001→0.001
API
parseNumber(value: string, options?: ParseOptions): number | null
Parses a string and returns a number or null if the input is invalid.
Parameters:
value(string): The string to parseoptions(ParseOptions, optional): Configuration options
Returns:
number: The parsed numbernull: If the input is invalid or empty
ParseOptions
interface ParseOptions {
/** Strip any Unicode currency symbol (e.g. $, €, ₪, ₿). Default: true */
stripCurrency?: boolean;
/** Allow Swiss-style apostrophe grouping (both ' and '). Default: true */
allowApostrophe?: boolean;
/** Allow scientific notation like 1.5e3. Default: false */
allowScientific?: boolean;
}Options Examples
// Default behavior
parseNumber(input, {
stripCurrency: true, // \p{Sc} removed by default
allowApostrophe: true, // Swiss ' and ' grouping
allowScientific: false // opt-in
});
// Disable currency stripping
parseNumber('$1,234.56', { stripCurrency: false }); // → null
parseNumber('1,234.56', { stripCurrency: false }); // → 1234.56
// Disable Swiss apostrophe format
parseNumber("1'234.56", { allowApostrophe: false }); // → null
parseNumber('1,234.56', { allowApostrophe: false }); // → 1234.56
// Enable scientific notation
parseNumber('1.5e3', { allowScientific: true }); // → 1500
parseNumber('1.5e-3', { allowScientific: true }); // → 0.0015
// Combine options
parseNumber('₪1'234.56', {
stripCurrency: true,
allowApostrophe: true,
allowScientific: false
}); // → 1234.56Validation Rules
The parser enforces strict validation rules:
- Thousands separators must be in groups of exactly 3 digits
- Decimal points can only appear once
- Signs (
+/-) can only appear at the beginning - Swiss apostrophes must follow proper 3-digit grouping
- Mixed separators are disambiguated by the last separator (decimal point)
Examples
// Valid inputs
parseNumber('1,234.56'); // ✅ 1234.56
parseNumber('1.234,56'); // ✅ 1234.56
parseNumber("1'234.56"); // ✅ 1234.56
parseNumber('1,000'); // ✅ 1000
parseNumber('1.000'); // ✅ 1000
parseNumber("1'000"); // ✅ 1000
parseNumber('-1,234.56'); // ✅ -1234.56
parseNumber('.5'); // ✅ 0.5
parseNumber(',5'); // ✅ 0.5
// Invalid inputs
parseNumber('1,23,456'); // ❌ null (invalid grouping)
parseNumber('1.23.456'); // ❌ null (invalid grouping)
parseNumber('1,234.56.78'); // ❌ null (multiple decimal points)
parseNumber('1-234.56'); // ❌ null (sign in middle)
parseNumber('1,234,56'); // ❌ null (ambiguous - could be 1,234,56 or 1,234.56)
parseNumber(''); // ❌ null (empty)
parseNumber('invalid'); // ❌ null (non-numeric)Performance Benchmarks
Real-world Performance
Running performance benchmark...
Total time: 1170.28ms
Average time per parse: 0.0002ms
Parses per second: 4,443,387
Total operations: 5,200,000Test Cases Covered
- US format:
1,234.56 - EU format:
1.234,56 - Swiss format:
1'234.56 - Scientific notation:
1.5e3 - Currency symbols:
₪1,234.56,1,234.56$ - Large numbers:
999,999,999.99 - Small decimals:
0.001
Optimization Features
- Pre-compiled regex patterns - No regex compilation overhead
- Method extraction - Clean, maintainable code structure
- Early returns - Minimal processing for invalid inputs
- Unicode optimization - Efficient
\p{Sc}class usage
Development
# Install dependencies
npm install
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Build the library
npm run build
# Development mode with watch
npm run devContributing
Contributions are welcome! Please open issues or pull requests on GitHub.
License
MIT © Norbert Lorincz
