mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 04:10:32 +00:00
131 lines
3.3 KiB
TypeScript
131 lines
3.3 KiB
TypeScript
const validGrouping = (integerPart, sep) =>
|
|
integerPart.split(sep).reduce((acc, group, idx) => {
|
|
if (idx > 0) {
|
|
return acc && group.length === 3;
|
|
}
|
|
|
|
return acc && group.length;
|
|
}, true);
|
|
|
|
export const multiNumberParse = (number: number | string, standardDecSep = '.') => {
|
|
// if it's a number already, this is going to be easy...
|
|
if (typeof number === 'number') {
|
|
return number;
|
|
}
|
|
|
|
// check validity of parameters
|
|
if (!number || typeof number !== 'string') {
|
|
throw new TypeError('number must be a string');
|
|
}
|
|
|
|
if (typeof standardDecSep !== 'string' || standardDecSep.length !== 1) {
|
|
throw new TypeError('standardDecSep must be a single character string');
|
|
}
|
|
|
|
// check if negative
|
|
const negative = number[0] === '-';
|
|
|
|
// strip unnecessary chars
|
|
const stripped = number
|
|
// get rid of trailing non-numbers
|
|
.replace(/[^\d]+$/, '')
|
|
// get rid of the signal
|
|
.slice(negative ? 1 : 0);
|
|
|
|
// analyze separators
|
|
const separators = (stripped.match(/[^\d]/g) || []).reduce(
|
|
(acc, sep, idx) => {
|
|
const sepChr = `str_${sep.codePointAt(0)}`;
|
|
const cnt = ((acc[sepChr] || {}).cnt || 0) + 1;
|
|
|
|
return {
|
|
...acc,
|
|
[sepChr]: {
|
|
sep,
|
|
cnt,
|
|
lastIdx: idx,
|
|
},
|
|
};
|
|
},
|
|
{}
|
|
);
|
|
|
|
// check correctness of separators
|
|
const sepKeys = Object.keys(separators);
|
|
|
|
if (!sepKeys.length) {
|
|
// no separator, that's easy-peasy
|
|
return parseInt(stripped, 10) * (negative ? -1 : 1);
|
|
}
|
|
|
|
if (sepKeys.length > 2) {
|
|
// there's more than 2 separators, that's wrong
|
|
return Number.NaN;
|
|
}
|
|
|
|
if (sepKeys.length > 1) {
|
|
// there's two separators, that's ok by now
|
|
let sep1 = separators[sepKeys[0]];
|
|
let sep2 = separators[sepKeys[1]];
|
|
|
|
if (sep1.lastIdx > sep2.lastIdx) {
|
|
// swap
|
|
[sep1, sep2] = [sep2, sep1];
|
|
}
|
|
|
|
// if more than one separator appears more than once, that's wrong
|
|
if (sep1.cnt > 1 && sep2.cnt > 1) {
|
|
return Number.NaN;
|
|
}
|
|
|
|
// check if the last separator is the single one
|
|
if (sep2.cnt > 1) {
|
|
return Number.NaN;
|
|
}
|
|
|
|
// check the groupings
|
|
const [integerPart] = stripped.split(sep2.sep);
|
|
|
|
if (!validGrouping(integerPart, sep1.sep)) {
|
|
return Number.NaN;
|
|
}
|
|
|
|
// ok, we got here! let's handle it
|
|
return (
|
|
parseFloat(stripped.split(sep1.sep).join('').replace(sep2.sep, '.')) *
|
|
(negative ? -1 : 1)
|
|
);
|
|
}
|
|
|
|
// ok, only one separator, which is nice
|
|
const sep = separators[sepKeys[0]];
|
|
|
|
if (sep.cnt > 1) {
|
|
// there's more than one separator, which means it's integer
|
|
// let's check the groupings
|
|
if (!validGrouping(stripped, sep.sep)) {
|
|
return Number.NaN;
|
|
}
|
|
|
|
// it's valid, let's return an integer
|
|
return parseInt(stripped.split(sep.sep).join(''), 10) * (negative ? -1 : 1);
|
|
}
|
|
|
|
// just one separator, let's check last group
|
|
const groups = stripped.split(sep.sep);
|
|
|
|
if (groups[groups.length - 1].length === 3) {
|
|
// ok, we're in ambiguous territory here
|
|
|
|
if (sep.sep !== standardDecSep) {
|
|
// it's an integer
|
|
return (
|
|
parseInt(stripped.split(sep.sep).join(''), 10) * (negative ? -1 : 1)
|
|
);
|
|
}
|
|
}
|
|
|
|
// well, it looks like it's a simple float
|
|
return parseFloat(stripped.replace(sep.sep, '.')) * (negative ? -1 : 1);
|
|
};
|