Last active
June 28, 2020 21:32
-
-
Save dfkaye/a9ae55375ecef4520800aaa7a67e2182 to your computer and use it in GitHub Desktop.
s2n.js: get numeric value contained in a string; currency-comma-whitespace-friendly version of parseInt et al.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 | |
// 8 March 2019: regexp with Number(n) output. | |
// 10 March 2019: updated with tests and a function name. | |
// 11 March 2019: change void 0 to null, fix comment mistakes. | |
// 28 June 2020: added trim and negation test so "-$200" returns -200, not 200. | |
/* | |
* @function s2n is a currency-comma-and-whitespace-friendly version of built-in | |
* functions Number, parseFloat(), and parseInt(value, radix) without the radix | |
* argument. | |
* | |
* Examples of built-in uselessness: | |
* | |
* Number("-1,020.9") returns NaN - can't handle commas. | |
* parseFloat("-1,020.9") returns -1 - exits at first comma. | |
* parseInt("-1,020.9") returns -1 - exits at first comma. | |
* | |
* With currencies, the problem is uniformly useless, across the board: | |
* | |
* Number("$1,020.9") returns NaN. | |
* parseFloat("$1,020.9") returns NaN. | |
* parseInt("$1,020.9") returns NaN. | |
* | |
* `NaN` is not a value, it is only information about a value's "type" - which | |
* we don't care about. We want the converted value, when one can be found, or | |
* a sensible fallback value such as `0` when one cannot. | |
* | |
* s2n processes all numeric characters, ignores all non-numerics, and returns | |
* the numeric value contained in the string. | |
* | |
* If a string has no value or results in `NaN`, s2n returns `0`. | |
* | |
* Examples: | |
* s2n("$200") returns the number, `200`. | |
* s2n("4,000.015") returns the number, `4000.015`. | |
* s2n("6 999 999 . 999") returns the number, `6999999.999`. | |
* s2n("7 a b 8") returns the number, `78`. | |
* s2n("$blah.blah") returns the number, `0`. | |
* s2n("-0") returns the number, `0`. | |
* s2n("-x") returns the number, `0`. | |
* s2n("-$200") returns the number, `-200`. | |
* | |
* Function does not process possible expressions, such as "1 + 1". s2n("1 + 1") | |
* returns the number, `11`, not the number `2`. | |
* | |
* Negative hexadecimal strings, e.g., "-0x333", are not well supported yet. | |
* | |
* @param {number|string} s | |
* @returns {number} | |
*/ | |
function s2n(s) { | |
var n = Number(s); | |
if (n === n) { | |
return n; | |
} | |
var t = String(s).trim(); | |
var m = t.match(/[\d\.]/g); | |
var v = m ? m.join('') : null; | |
n = Number(v); | |
// return number or 0, not -0, not NaN | |
return n === n | |
? t.charAt(0) === "-" && n | |
? n * -1 | |
: n + 0 | |
: 0; | |
} | |
/* test it out */ | |
var tests = [ | |
// decimal with no leading 0 | |
".01", | |
// 0 is 0 | |
"0.0", | |
// decimal with leading zero | |
"0.99", | |
// octal notation | |
"08", | |
// integer | |
"1", | |
// currency | |
"$200", | |
// decimal | |
"300.015", | |
// comma | |
"4,000.015", | |
// whitespace | |
"5 999 999 . 999", | |
// larger than maximum safe integer | |
"9478123289889839898438984843498934894", | |
// alpha numeric | |
"7 a b 8", | |
// no value | |
"$blah.blah", | |
// no value | |
"x", | |
// operation is not a value | |
"1 + 1", | |
// a number | |
555, | |
// hexadecimal number | |
0x333, | |
// hexadecimal string | |
"0x333" | |
]; | |
var results = [].concat( | |
"*positive values*", | |
tests.map(function(s) { return { input: String(s), result: s2n(s) }; }), | |
"*negative values*", | |
tests.map(function(s) { return { input: '-' + s, result: s2n('-' + s) }; }) | |
); | |
console.dir(results); | |
/*Array(36) | |
0: "*positive values*" | |
1: {input: ".01", result: 0.01} | |
2: {input: "0.0", result: 0} | |
3: {input: "0.99", result: 0.99} | |
4: {input: "08", result: 8} | |
5: {input: "1", result: 1} | |
6: {input: "$200", result: 200} | |
7: {input: "300.015", result: 300.015} | |
8: {input: "4,000.015", result: 4000.015} | |
9: {input: "5 999 999 . 999", result: 5999999.999} | |
10: {input: "9478123289889839898438984843498934894", result: 9.47812328988984e+36} | |
11: {input: "7 a b 8", result: 78} | |
12: {input: "$blah.blah", result: 0} | |
13: {input: "x", result: 0} | |
14: {input: "1 + 1", result: 11} | |
15: {input: "555", result: 555} | |
16: {input: "819", result: 819} // Surprising result: 0x333 is converted to decimal | |
17: {input: "0x333", result: 819} // Surprising result: "0x333" is converted to decimal | |
18: "*negative values*" | |
19: {input: "-.01", result: -0.01} | |
20: {input: "-0.0", result: -0} | |
21: {input: "-0.99", result: -0.99} | |
22: {input: "-08", result: -8} | |
23: {input: "-1", result: -1} | |
24: {input: "-$200", result: -200} | |
25: {input: "-300.015", result: -300.015} | |
26: {input: "-4,000.015", result: -4000.015} | |
27: {input: "-5 999 999 . 999", result: -5999999.999} | |
28: {input: "-9478123289889839898438984843498934894", result: -9.47812328988984e+36} | |
29: {input: "-7 a b 8", result: -78} | |
30: {input: "-$blah.blah", result: 0} | |
31: {input: "-x", result: 0} | |
32: {input: "-1 + 1", result: -11} | |
33: {input: "-555", result: -555} | |
34: {input: "-819", result: -819} // Surprising result: -0x333 is converted to decimal | |
35: {input: "-0x333", result: -333} // Surprising result: Number("-0x333") returns NaN | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment