diff --git a/src/floatify.js b/src/floatify.js index 017af51..e59e523 100644 --- a/src/floatify.js +++ b/src/floatify.js @@ -1,39 +1,44 @@ -'use strict'; +module.exports = class floatify { + toFloatFormat({ string, thousandsSeperator = '', decimalSeperator = '' }) { + let returnString = string; -var floatify = function floatify(str) { - var toFloatFormat = function toFloatFormat(str, ts, ds) { - var string = str; - var thousandsSeparator = ts || ''; - var decimalSeparator = ds || ''; + if (!!thousandsSeperator) { + returnString = returnString.replace(new RegExp(this.escapeForRegExp(thousandsSeperator), 'g'), ''); + } + + if (!!decimalSeperator) { + returnString = returnString.replace(new RegExp(this.escapeForRegExp(decimalSeperator), 'g'), '.'); + } + + return parseFloat(returnString); + } - thousandsSeparator = thousandsSeparator === '.' ? '\\.' : thousandsSeparator; - decimalSeparator = decimalSeparator === '.' ? '\\.' : decimalSeparator; + escapeForRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } - string = thousandsSeparator !== '' ? string.replace(new RegExp(thousandsSeparator, 'g'), '') : string; - string = decimalSeparator !== '' ? string.replace(new RegExp(decimalSeparator, 'g'), '.') : string; + parseParts({ string, element, count }) { + const parts = string.split(element); - return parseFloat(string); - }; + for (let i = 1; i < parts.length; i++) { + const left = { + index: i - 1, + value: parts[i - 1], + parsed: parseInt(parts[i - 1], 10) + }; - var parseParts = function parseParts(str, ele, count) { - var string = str; - var element = ele; - var parts = string.split(element); - for (var i = 1; i < parts.length; i++) { - var left = parseInt(parts[i - 1], 10); + const part = parts[i]; - if (parts[i].length === 0) { + if (part.length === 0) { return Number.NaN; - } else if (parts[i].length === 3) { - if (parts[i - 1].length > 3 && parts.length - 1 !== i) { + } else if (part.length === 3) { + if (left.value.length > 3 && parts.length - 1 !== i) { return Number.NaN; - } - - if ( - (left === 0 || isNaN(left) || parts[i - 1].length > 3) + } else if ( + (left.parsed === 0 || isNaN(left.parsed) || left.value.length > 3) && parts.length - 1 === i ) { - return toFloatFormat(string, '', element); + return this.toFloatFormat({ string, thousandsSeperator: '', decimalSeperator: element }); } } else if (i < parts.length - 1) { // violation in midPart -> NaN @@ -41,111 +46,83 @@ var floatify = function floatify(str) { } else { // violation in end -> Could be decimalSeparator if (count === 1) { - return toFloatFormat(string, '', element); + return this.toFloatFormat({ string, thousandsSeperator: '', decimalSeperator: element }); } return Number.NaN; } } - return toFloatFormat(string, element, ''); - }; - - var parse = function parse(str) { - var string = str; - var spacePos; - var spaceSplit; - var spaceCount; - var dotPos; - var commaPos; - var lDotPos; - var lCommaPos; - var dotCount; - var commaCount; - - string = string.trim(); - - // 1st dot position - dotPos = string.indexOf('.'); - // 1st comma position - commaPos = string.indexOf(','); - // 1st space position - spacePos = string.indexOf(' '); - - if (dotPos + commaPos + spacePos === -3) { - // life is good, no separators - return toFloatFormat(string); - } + return this.toFloatFormat({ string, thousandsSeperator: element, decimalSeperator: '' }); + } - // space count - spaceSplit = string.split(' '); - spaceCount = spaceSplit.length - 1; + parse(input) { + let string = input.trim(); - if (spaceCount > 0) { - var first = spaceSplit.shift(); - var last = spaceSplit.pop(); + const hasSpace = string.includes(' '); + const spaceSplit = string.split(' '); + const spaceCount = spaceSplit.length - 1; - if (!string.match(/^(\d{1,3})?(\s\d{3})*([,\.]\d+)?$/)) { - return Number.NaN; - } + const hasDot = string.includes('.'); + const dotPos = string.indexOf('.'); + const dotCount = string.split('.').length - 1; + let lDotPos; - spaceSplit.unshift(first); - spaceSplit.push(last); + const hasComma = string.includes(','); + const commaPos = string.indexOf(','); + const commaCount = string.split(',').length - 1; + let lCommaPos; - string = spaceSplit.join(''); - } - // dot count - dotCount = string.split('.').length - 1; - // comma count - commaCount = string.split(',').length - 1; + if (hasDot || hasComma || hasSpace) { + if (spaceCount > 0) { + const first = spaceSplit.shift(); + const last = spaceSplit.pop(); - if (dotPos !== -1 && commaPos !== -1) { - // format is using dot and comma + if (!string.match(/^(\d{1,3})?(\s\d{3})*([,\.]\d+)?$/)) { + return Number.NaN; + } - // last dot position - lDotPos = string.lastIndexOf('.'); - // last comma position - lCommaPos = string.lastIndexOf(','); + spaceSplit.unshift(first); + spaceSplit.push(last); - // order of 1st dot -> comma must be same as last dot -> comma - // 123.123.123,123 -> ok 123.123,123.123 -> not ok - if (Math.sign(dotPos - commaPos) !== Math.sign(lDotPos - lCommaPos)) { - return Number.NaN; + string = spaceSplit.join(''); } - // check positions to guess the thousands separator - if (dotPos > commaPos && dotCount === 1) { + if (hasDot && hasComma) { + // format is using dot and comma + + // last dot position + lDotPos = string.lastIndexOf('.'); + // last comma position + lCommaPos = string.lastIndexOf(','); + + // order of 1st dot -> comma must be same as last dot -> comma + // 123.123.123,123 -> ok 123.123,123.123 -> not ok + if (Math.sign(dotPos - commaPos) !== Math.sign(lDotPos - lCommaPos)) { + return Number.NaN; + } + + // check positions to guess the thousands separator + if (dotPos > commaPos && dotCount === 1) { // best guess: . is thousands separator and , is decimal point - return toFloatFormat(string, ',', '.'); - } + return this.toFloatFormat({ string, thousandsSeperator: ',', decimalSeperator: '.' }); + } - if (commaPos > dotPos && commaCount === 1) { + if (commaPos > dotPos && commaCount === 1) { // best guess: , is thousands separator and . is decimal point - return toFloatFormat(string, '.', ','); - } - - return Number.NaN; - } + return this.toFloatFormat({ string, thousandsSeperator: '.', decimalSeperator: ',' }); + } - if (dotPos !== -1) { + return Number.NaN; + } else if (hasDot) { // only dot(s) in format - return parseParts(string, '.', dotCount); - } - - if (commaPos !== -1) { + return this.parseParts({ string, element: '.', count: dotCount }); + } else if (hasComma) { // only comma(s) in format - return parseParts(string, ',', commaCount); + return this.parseParts({ string, element: ',', count: commaCount }); + } } - return toFloatFormat(string); - }; - - return parse(str); -}; - -if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = floatify; + return this.toFloatFormat({ string }); } - exports.floatify = floatify; -} +}; diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 0000000..7eeefc3 --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,5 @@ +{ + "env": { + "mocha": true + } +} diff --git a/test/floatify.js b/test/floatify.js index f9de21a..6b6b166 100644 --- a/test/floatify.js +++ b/test/floatify.js @@ -1,7 +1,8 @@ 'use strict'; -var floatify = require('../src/floatify.js'); -var assert = require('assert'); +const Floatify = require('../src/floatify.js'); +const floatify = new Floatify(); +const assert = require('assert'); const tests = [ { @@ -289,13 +290,12 @@ tests.forEach((test) => { it(`${assertion.input} gives ${assertion.expectation}`, () => { if (Number.isNaN(assertion.expectation)) { assert.strictEqual( - isNaN(floatify.floatify(assertion.input)), + isNaN(floatify.parse(assertion.input)), true ); - return; + } else { + assert.equal(floatify.parse(assertion.input), assertion.expectation); } - - assert.equal(floatify.floatify(assertion.input), assertion.expectation); }); }); });