├── .gitattributes ├── .npmignore ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── benchmark ├── original.js └── index.js ├── package.json ├── HISTORY.md ├── README.md ├── index.js ├── docs └── benchmark.md └── test.js /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | benchmark/ 3 | coverage/ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Dependency directory 6 | node_modules 7 | 8 | # OS X 9 | .DS_Store 10 | coverage/ 11 | .nyc_output/ 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | node_js: 10 | - '14' 11 | 12 | script: npm run coverage 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2021 Eli Doran 2 | Copyright (c) 2015 Cesar Andreu 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /benchmark/original.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Comma number formatter 3 | * @param {Number} number Number to format 4 | * @param {String} [separator=','] Value used to separate numbers 5 | * @returns {String} Comma formatted number 6 | */ 7 | module.exports = function commaNumber (number, separator) { 8 | separator = typeof separator === 'undefined' ? ',' : ('' + separator) 9 | 10 | // Convert to number if it's a non-numeric value 11 | if (typeof number !== 'number') { 12 | number = Number(number) 13 | } 14 | 15 | // NaN => 0 16 | if (isNaN(number)) { 17 | number = 0 18 | } 19 | 20 | // Return Infinity immediately 21 | if (!isFinite(number)) { 22 | return '' + number 23 | } 24 | 25 | var stringNumber = ('' + Math.abs(number)) 26 | .split('') 27 | .reverse() 28 | 29 | var result = [] 30 | for (var i = 0; i < stringNumber.length; i++) { 31 | if (i && i % 3 === 0) { 32 | result.push(separator) 33 | } 34 | result.push(stringNumber[i]) 35 | } 36 | 37 | // Handle negative numbers 38 | if (number < 0) { 39 | result.push('-') 40 | } 41 | 42 | return result 43 | .reverse() 44 | .join('') 45 | } 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "comma-number", 3 | "version": "2.1.0", 4 | "description": "Format a number with commas or custom character", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js" 8 | ], 9 | "scripts": { 10 | "test": "tap test.js", 11 | "test10": "nave use 10 tap --no-coverage test.js", 12 | "test12": "nave use 12 tap --no-coverage test.js", 13 | "test14": "nave use 14 tap --no-coverage test.js", 14 | "tests": "npm run test10 && npm run test12 && npm run test14", 15 | "benchmark": "node benchmark/index.js", 16 | "coverage": "npm run tests && tap --coverage-report=lcovonly test.js && cat coverage/lcov.info | coveralls", 17 | "coverage-clean": "rm -rf ./coverage ./.nyc_output" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/elidoran/comma-number.git" 22 | }, 23 | "keywords": [ 24 | "number", 25 | "format", 26 | "comma" 27 | ], 28 | "author": { 29 | "name": "Eli Doran", 30 | "email": "eli+npm@elidoran.com", 31 | "url": "https://github.com/elidoran" 32 | }, 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/elidoran/comma-number/issues" 36 | }, 37 | "homepage": "https://github.com/elidoran/comma-number#readme", 38 | "devDependencies": { 39 | "benchmark": "^2.1.4", 40 | "console.table": "^0.10.0", 41 | "coveralls": "^3.1.0", 42 | "nave": "^3.2.2", 43 | "pad": "^3.2.0", 44 | "tap": "^15.0.5" 45 | }, 46 | "dependencies": {} 47 | } 48 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 2.1.0 - 2021/04/30 2 | ================== 3 | 4 | 1. tweak the flow a bit so the `separator` and `decimal` value are all in an array which is turned into a string via `join('')`. so, all at once the parts are turned into the final string. 5 | 2. added some local scopes to the format. 6 | 3. changed how it calculates where to start putting separators. 7 | 4. switched a while-loop with an increment in the block to a for-loop with the increment in its loop definition. 8 | 5. fixed a bug in the `benchmark/index.js` file when using the `--delta` option. 9 | 6. added more (longer) numbers to the tests 10 | 11 | 12 | 2.0.2 - 2021/04/28 13 | 14 | 1. update dev dependencies 15 | 2. add 2021 to copyright 16 | 3. reformatted var usage in test/lib/index.js and added new test showing a bug 17 | 4. fixed bug when a number input was given for a `decimalChar` other than `'.'`. 18 | 5. added node 14 to testing 19 | 6. switched from tape to tap for testing and removed istanbul 20 | 7. added `.nyc_output` to git ignore 21 | 8. updated travis config to use a single VM and use nave to run tests with multiple node versions. 22 | 9. add `npm install` to command list in docs/benchmark.md. 23 | 24 | 2.0.1 - 2019/04/19 25 | 26 | 1. update dev dependencies 27 | 2. add 2018 and 2019 to copyright 28 | 3. remove lcov only coverage script 29 | 4. add nave dev dependency and use it in test scripts for node 4-12 (evens) 30 | 5. use const/let instead of var 31 | 6. use more descriptive variable names 32 | 7. reformat a bit 33 | 8. update benchmark script to report PID and ask before starting so the process can be set to a high priority 34 | 35 | 2.0.0 - 2017/04/02 36 | ================== 37 | 38 | 1. revised implementation 39 | 2. supports decimals 40 | 3. more test cases 41 | 4. updated travis with modern node versions 42 | 5. added badges to README 43 | 6. updated README content with change info, new `bindWith()`, benchmark info. 44 | 7. added code coverage 45 | 8. dropped 'standard' lint 46 | 9. added benchmarking 47 | 10. added a document showing the benchmark output (docs/benchmark.md) 48 | 49 | 50 | 1.1.0 / 2015-07-16 51 | ================== 52 | 53 | * Use isFinite instead of Number.isFinite for improved browser compatibility 54 | 55 | 1.0.0 / 2015-05-12 56 | ================== 57 | 58 | * Initial release 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Comma number 2 | [![Build Status](https://travis-ci.org/elidoran/comma-number.svg?branch=master)](https://travis-ci.org/elidoran/comma-number) 3 | [![npm version](https://badge.fury.io/js/comma-number.svg)](http://badge.fury.io/js/comma-number) 4 | [![Coverage Status](https://coveralls.io/repos/github/elidoran/comma-number/badge.svg?branch=master)](https://coveralls.io/github/elidoran/comma-number?branch=master) 5 | 6 | 7 | ## Install 8 | 9 | ```sh 10 | $ npm install --save comma-number 11 | ``` 12 | 13 | 14 | ## Usage 15 | 16 | ```js 17 | const commaNumber = require('comma-number') 18 | 19 | commaNumber(1000) // "1,000" 20 | commaNumber(-1000) // "-1,000" 21 | commaNumber(-1000, '.') // "-1.000" 22 | 23 | commaNumber(1000.12) // "1,000.12" 24 | commaNumber(-1000.12) // "-1,000.12" 25 | commaNumber('-1000,12', '.', ',') // "-1.000,12" 26 | 27 | // make a new function using custom separator and decimal char: 28 | const format = commaNumber.bindWith('_', '!') 29 | // use it as you would commaNumber(). 30 | format(1000) // "1_000" 31 | format(-1000) // "-1_000" 32 | format(1000.12) // "1_000!12" 33 | format(-1000.12) // "-1_000!12" 34 | ``` 35 | 36 | 37 | ## Version 2 Changes 38 | 39 | Revised implementation changes the API a bit: 40 | 41 | 1. input with a type other than `string` and `number` is returned **as is**, not as `'0'`. 42 | 2. supports decimals in the number 43 | 3. a string number may use an alternate decimal character, specify it as the third argument 44 | 4. added a `bindWith` function to use a currying style to bind options for a reusable format function. 45 | 46 | Other changes: 47 | 48 | 1. Added benchmarking to test implementation performance 49 | 2. added code coverage 50 | 3. added new badges in this README 51 | 4. added more versions to the Travis CI config 52 | 53 | 54 | ## API 55 | 56 | ### commaNumber(number, [separator=','], [decimalChar='.']) 57 | 58 | **Parameters:** 59 | 60 | * number : {(Number|String)} Number to format 61 | * separator : {String} Value used to separate numbers 62 | * decimalChar : {String} Value used to separate the decimal value 63 | 64 | **Returns:** 65 | 66 | * {String} Comma formatted number 67 | 68 | 69 | ### bindWith(separator, decimalChar) 70 | 71 | The `commaNumber` function accepts these same parameters as the second and third params. This prevents using currying to bind them and reuse that bound function. 72 | 73 | The `bindWith` function accepts the options and returns a function bound with them. 74 | 75 | ```javascript 76 | // the default commaNumber uses a comma separator and period for decimal char. 77 | var commaNumber = require('comma-number') 78 | // can build a custom version using bindWith. 79 | , format = commaNumber.bindWith('_', '!') 80 | , result1 = commaNumber(1234567.89) 81 | , result2 = format('1234567.89') 82 | 83 | console.log(result1) // outputs: 1,234,567.89 84 | console.log(result2) // outputs: 1_234_567!89 85 | ``` 86 | 87 | 88 | ## Scripts for Testing, Benchmarking, and Code Coverage 89 | 90 | ```sh 91 | # run tests via tap 92 | $ npm test 93 | 94 | # benchmark current implementation versus previous 95 | npm run benchmark 96 | 97 | # get coverage info by default with testing: 98 | npm test 99 | ``` 100 | 101 | 102 | ## Performance Comparison 103 | 104 | The rewrite has a considerable performance increase from the previous version. 105 | 106 | I converted the benchmark output from my machine into a [table](docs/benchmark.md). 107 | 108 | It compares the performance of version 1.1.0 with 2.0.0. The inputs with decimals can only be processed by the new version so those show as "invalid" for the previous version. 109 | 110 | 111 | ## [MIT License](LICENSE.md) 112 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // return a string with the provided number formatted with commas. 4 | // can specify either a Number or a String. 5 | function commaNumber(inputNumber, optionalSeparator, optionalDecimalChar) { 6 | 7 | // default `decimalChar` is a period 8 | const decimalChar = optionalDecimalChar || '.' 9 | 10 | let stringNumber // we assign this in the switch block and need it later. 11 | 12 | { 13 | let number // we assign this in the switch block and need it right after. 14 | 15 | switch (typeof inputNumber) { 16 | 17 | case 'string': 18 | 19 | // if there aren't enough digits to need separators then return it 20 | // NOTE: some numbers which are too small will get passed this 21 | // when they have decimal values which make them too long here. 22 | // but, the number value check after this switch will catch it. 23 | if (inputNumber.length < (inputNumber[0] === '-' ? 5 : 4)) { 24 | return inputNumber 25 | } 26 | 27 | // remember it as a string in `stringNumber` and convert to a Number 28 | stringNumber = inputNumber 29 | 30 | // if they're not using the Node standard decimal char then replace it 31 | // before converting. 32 | number = Number( 33 | (decimalChar !== '.') ? stringNumber.replace(decimalChar, '.') : stringNumber 34 | ) 35 | break 36 | 37 | // convert to a string. 38 | // NOTE: don't check if the number is too small before converting 39 | // because we'll need to return `stringNumber` anyway. 40 | case 'number': 41 | stringNumber = String(inputNumber) 42 | number = inputNumber 43 | // create the string version with the decimalChar they specified. 44 | // this matches what the above case 'string' produces, 45 | // and, fixes the bug *not* doing this caused. 46 | if ('.' !== decimalChar && !Number.isInteger(inputNumber)) { 47 | stringNumber = stringNumber.replace('.', decimalChar) 48 | } 49 | break 50 | 51 | // return invalid type as-is 52 | default: return inputNumber 53 | } 54 | 55 | // when it doesn't need a separator or isn't a number then return it 56 | if ((-1000 < number && number < 1000) || isNaN(number) || !isFinite(number)) { 57 | return stringNumber 58 | } 59 | } 60 | 61 | { 62 | // strip off decimal value to add back in later 63 | const decimalIndex = stringNumber.lastIndexOf(decimalChar) 64 | let decimal 65 | if (decimalIndex > -1) { 66 | decimal = stringNumber.slice(decimalIndex) 67 | stringNumber = stringNumber.slice(0, decimalIndex) 68 | } 69 | 70 | // finally, parse the string. Note, default 'separator' is a comma. 71 | const parts = parse(stringNumber, optionalSeparator || ',') 72 | 73 | // if there's a decimal value then add it to the parts. 74 | if (decimal) {// NOTE: we sliced() it off including the decimalChar 75 | parts.push(decimal) 76 | } 77 | 78 | // combine all parts for the final string (note, has separators). 79 | return parts.join('') 80 | } 81 | } 82 | 83 | function parse(string, separator) { 84 | 85 | // find first index to split the string at (where 1st separator goes). 86 | let i = ((string.length - 1) % 3) + 1 87 | 88 | // above calculation is wrong when num is negative and a certain size. 89 | if (i === 1 && (string[0] === '-')) { 90 | i = 4 // example: -123,456,789 start at 4, not 1. 91 | } 92 | 93 | const strings = [ // holds the string parts 94 | string.slice(0, i) // grab part before the first separator 95 | ] 96 | 97 | // split remaining string in groups of 3 where a separator belongs 98 | for (; i < string.length; i += 3) { 99 | strings.push(separator, string.substr(i, 3)) 100 | } 101 | 102 | return strings 103 | } 104 | 105 | 106 | // convenience function for currying style: 107 | // const format = commaNumber.bindWith(',', '.') 108 | function bindWith(separator, decimalChar) { 109 | return function(number) { 110 | return commaNumber(number, separator, decimalChar) 111 | } 112 | } 113 | 114 | module.exports = commaNumber 115 | module.exports.bindWith = bindWith 116 | -------------------------------------------------------------------------------- /docs/benchmark.md: -------------------------------------------------------------------------------- 1 | # benchmark output as a table 2 | 3 | Compares performance of version 1.1.0 versus 2.0.0 measured by BenchmarkJS. 4 | 5 | 1. input - the input provided to each implementation 6 | 2. old - the previous version, 1.1.0 7 | 3. new - the current version, 2.0.0 8 | 4. '+' - the change from 'old' to 'new' `((new - old) / old)` 9 | 10 | (I used the names 'old' and 'new' because they're the same length for the console output during run which prints as each one completes). 11 | 12 | Run this for yourself via: 13 | 14 | ```sh 15 | git clone https://github.com/elidoran/comma-number.git 16 | cd comma-number 17 | npm install 18 | npm run benchmark 19 | ``` 20 | 21 | I converted the output to a table instead of using a screenshot because: 22 | 23 | 1. it's very long 24 | 2. it's better in the repo as text to update than an image file 25 | 26 | ## Table 27 | 28 | | input | old | new | **+** | 29 | | -----------------:|-------------:|------------:|--------:| 30 | | '1' | 4,321,660 | 25,292,907 | 485% | 31 | | '12' | 2,335,091 | 25,279,395 | 983% | 32 | | '123' | 2,215,699 | 25,280,389 | 1041% | 33 | | '1234' | 1,898,418 | 2,717,567 | 43% | 34 | | '12345' | 1,803,474 | 2,646,915 | 47% | 35 | | '123456' | 1,650,963 | 2,647,299 | 60% | 36 | | '1234567' | 1,496,245 | 2,391,898 | 60% | 37 | | '12345678' | 1,142,649 | 1,790,405 | 57% | 38 | | '123456789' | 1,106,347 | 1,771,035 | 60% | 39 | | '1234567890' | 1,023,218 | 1,667,711 | 63% | 40 | | '12345678901' | 910,813 | 1,367,909 | 50% | 41 | | '123456789012' | 867,090 | 1,365,369 | 57% | 42 | | '-1' | 2,411,602 | 25,421,983 | 954% | 43 | | '-12' | 2,115,630 | 25,416,782 | 1101% | 44 | | '-123' | 1,985,855 | 25,490,377 | 1184% | 45 | | '-1234' | 1,723,408 | 2,521,618 | 46% | 46 | | '-12345' | 1,640,526 | 2,511,340 | 53% | 47 | | '-123456' | 1,506,253 | 2,477,339 | 64% | 48 | | '-1234567' | 1,379,279 | 2,218,996 | 61% | 49 | | '-12345678' | 1,302,015 | 2,166,128 | 66% | 50 | | '-123456789' | 1,240,217 | 2,188,525 | 76% | 51 | | '-1234567890' | 931,515 | 1,402,964 | 51% | 52 | | '-12345678901' | 893,546 | 1,360,492 | 52% | 53 | | '-123456789012' | 852,291 | 1,351,246 | 59% | 54 | | 1 | 4,490,943 | 22,922,986 | 410% | 55 | | 12 | 2,407,925 | 22,823,602 | 848% | 56 | | 123 | 2,250,508 | 22,924,160 | 919% | 57 | | 1234 | 1,927,439 | 2,825,233 | 47% | 58 | | 12345 | 1,822,013 | 2,648,682 | 45% | 59 | | 123456 | 1,675,451 | 2,609,400 | 56% | 60 | | 1234567 | 1,515,779 | 2,467,017 | 63% | 61 | | 12345678 | 1,417,092 | 2,344,859 | 65% | 62 | | 123456789 | 1,355,513 | 2,319,054 | 71% | 63 | | 1234567890 | 1,239,985 | 2,176,519 | 76% | 64 | | 12345678901 | 849,303 | 1,248,418 | 47% | 65 | | 123456789012 | 812,314 | 1,252,207 | 54% | 66 | | -1 | 2,621,460 | 22,925,704 | 775% | 67 | | -12 | 2,278,395 | 22,927,460 | 906% | 68 | | -123 | 2,131,312 | 22,904,668 | 975% | 69 | | -1234 | 1,830,496 | 2,636,265 | 44% | 70 | | -12345 | 1,741,359 | 2,599,531 | 49% | 71 | | -123456 | 1,617,002 | 2,610,976 | 61% | 72 | | -1234567 | 1,457,942 | 2,319,680 | 59% | 73 | | -12345678 | 1,366,607 | 2,319,183 | 70% | 74 | | -123456789 | 1,308,331 | 2,277,656 | 74% | 75 | | -1234567890 | 1,196,768 | 2,071,324 | 73% | 76 | | -12345678901 | 834,994 | 1,260,339 | 51% | 77 | | -123456789012 | 792,373 | 1,251,785 | 58% | 78 | | '1.2' | 1,490,516 | 25,353,848 | 1601% | 79 | | '12.3' | invalid | 8,261,296 | N/A | 80 | | '123.4' | invalid | 7,956,188 | N/A | 81 | | '1234.5' | invalid | 1,323,602 | N/A | 82 | | '12345.6' | invalid | 1,311,006 | N/A | 83 | | '123456.7' | invalid | 1,304,573 | N/A | 84 | | '1234567.8' | invalid | 1,219,565 | N/A | 85 | | '12345678.9' | invalid | 1,218,523 | N/A | 86 | | '123456789.0' | invalid | 1,367,902 | N/A | 87 | | '1234567890.1' | invalid | 1,262,388 | N/A | 88 | | '12345678901.2' | invalid | 1,246,678 | N/A | 89 | | '123456789012.3' | invalid | 1,248,526 | N/A | 90 | | '-1.2' | 1,426,902 | 25,499,637 | 1687% | 91 | | '-12.3' | invalid | 8,251,000 | N/A | 92 | | '-123.4' | invalid | 8,063,694 | N/A | 93 | | '-1234.5' | invalid | 1,330,762 | N/A | 94 | | '-12345.6' | invalid | 1,315,983 | N/A | 95 | | '-123456.7' | invalid | 1,305,668 | N/A | 96 | | '-1234567.8' | invalid | 1,227,211 | N/A | 97 | | '-12345678.9' | invalid | 1,349,881 | N/A | 98 | | '-123456789.0' | invalid | 1,363,914 | N/A | 99 | | '-1234567890.1' | invalid | 1,248,929 | N/A | 100 | | '-12345678901.2' | invalid | 1,244,452 | N/A | 101 | | '-123456789012.3' | invalid | 1,234,068 | N/A | 102 | | 1.2 | 1,269,245 | 4,818,785 | 280% | 103 | | 12.3 | invalid | 4,654,728 | N/A | 104 | | 123.4 | invalid | 4,822,503 | N/A | 105 | | 1234.5 | invalid | 1,175,543 | N/A | 106 | | 12345.6 | invalid | 1,195,957 | N/A | 107 | | 123456.7 | invalid | 1,194,760 | N/A | 108 | | 1234567.8 | invalid | 1,112,158 | N/A | 109 | | 12345678.9 | invalid | 1,126,245 | N/A | 110 | | 123456789.1 | invalid | 1,255,677 | N/A | 111 | | 1234567890.1 | invalid | 1,165,026 | N/A | 112 | | 12345678901.2 | invalid | 1,189,248 | N/A | 113 | | 123456789012.3 | invalid | 1,171,412 | N/A | 114 | | -1.2 | 1,225,972 | 4,839,886 | 295% | 115 | | -12.3 | invalid | 4,818,502 | N/A | 116 | | -123.4 | invalid | 4,850,423 | N/A | 117 | | -1234.5 | invalid | 1,195,494 | N/A | 118 | | -12345.6 | invalid | 1,193,016 | N/A | 119 | | -123456.7 | invalid | 1,195,036 | N/A | 120 | | -1234567.8 | invalid | 1,128,043 | N/A | 121 | | -12345678.9 | invalid | 1,254,578 | N/A | 122 | | -123456789.1 | invalid | 1,249,335 | N/A | 123 | | -1234567890.1 | invalid | 1,173,335 | N/A | 124 | | -12345678901.2 | invalid | 1,182,431 | N/A | 125 | | -123456789012.3 | invalid | 1,174,234 | N/A | 126 | 127 | 128 | ## Benchmarking Future 129 | 130 | This benchmark script is helpful for comparing new changes to the pre-change version. 131 | 132 | It just happens to be starting out as a way to compare the newest major version with the previous one. 133 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | var inspect, pad, Benchmark, suite, revised, original, methods, results, useDelta, columns, row, oldOps, INVALID, NA, inputs 2 | 3 | require('console.table') 4 | 5 | inspect = require('util').inspect 6 | pad = require('pad') 7 | Benchmark = require('benchmark') 8 | suite = new Benchmark.Suite 9 | revised = require('../index.js') 10 | original = require('./original.js') 11 | methods = [original, revised] 12 | results = [] 13 | useDelta = process.argv.indexOf('--delta') > -1 14 | columns = (methods.length * (useDelta ? 2 : 1)) + 2 15 | row = 0 16 | oldOps = -1 17 | INVALID = '! invalid' 18 | NA = ' N/A' 19 | 20 | inputs = [ 21 | 22 | [ [ '1' ], '1' ], 23 | [ [ '12' ], '12' ], 24 | [ [ '123' ], '123' ], 25 | [ [ '1234' ], '1,234' ], 26 | [ [ '12345' ], '12,345' ], 27 | [ [ '123456' ], '123,456' ], 28 | [ [ '1234567' ], '1,234,567' ], 29 | [ [ '12345678' ], '12,345,678' ], 30 | [ [ '123456789' ], '123,456,789' ], 31 | [ [ '1234567890' ], '1,234,567,890' ], 32 | [ [ '12345678901' ], '12,345,678,901' ], 33 | [ [ '123456789012' ], '123,456,789,012' ], 34 | 35 | [ [ '-1' ], '-1' ], 36 | [ [ '-12' ], '-12' ], 37 | [ [ '-123' ], '-123' ], 38 | [ [ '-1234' ], '-1,234' ], 39 | [ [ '-12345' ], '-12,345' ], 40 | [ [ '-123456' ], '-123,456' ], 41 | [ [ '-1234567' ], '-1,234,567' ], 42 | [ [ '-12345678' ], '-12,345,678' ], 43 | [ [ '-123456789' ], '-123,456,789' ], 44 | [ [ '-1234567890' ], '-1,234,567,890' ], 45 | [ [ '-12345678901' ], '-12,345,678,901' ], 46 | [ [ '-123456789012' ], '-123,456,789,012' ], 47 | 48 | [ [ 1 ], '1' ], 49 | [ [ 12 ], '12' ], 50 | [ [ 123 ], '123' ], 51 | [ [ 1234 ], '1,234' ], 52 | [ [ 12345 ], '12,345' ], 53 | [ [ 123456 ], '123,456' ], 54 | [ [ 1234567 ], '1,234,567' ], 55 | [ [ 12345678 ], '12,345,678' ], 56 | [ [ 123456789 ], '123,456,789' ], 57 | [ [ 1234567890 ], '1,234,567,890' ], 58 | [ [ 12345678901 ], '12,345,678,901' ], 59 | [ [ 123456789012 ], '123,456,789,012' ], 60 | 61 | [ [ -1 ], '-1' ], 62 | [ [ -12 ], '-12' ], 63 | [ [ -123 ], '-123' ], 64 | [ [ -1234 ], '-1,234' ], 65 | [ [ -12345 ], '-12,345' ], 66 | [ [ -123456 ], '-123,456' ], 67 | [ [ -1234567 ], '-1,234,567' ], 68 | [ [ -12345678 ], '-12,345,678' ], 69 | [ [ -123456789 ], '-123,456,789' ], 70 | [ [ -1234567890 ], '-1,234,567,890' ], 71 | [ [ -12345678901 ], '-12,345,678,901' ], 72 | [ [ -123456789012 ], '-123,456,789,012' ], 73 | 74 | [ ['1.2'], '1.2' ], 75 | [ ['12.3'], '12.3' ], 76 | [ ['123.4'], '123.4' ], 77 | [ ['1234.5'], '1,234.5' ], 78 | [ ['12345.6'], '12,345.6' ], 79 | [ ['123456.7'], '123,456.7' ], 80 | [ ['1234567.8'], '1,234,567.8' ], 81 | [ ['12345678.9'], '12,345,678.9' ], 82 | [ ['123456789.0'], '123,456,789.0' ], 83 | [ ['1234567890.1'], '1,234,567,890.1' ], 84 | [ ['12345678901.2'], '12,345,678,901.2' ], 85 | [ ['123456789012.3'], '123,456,789,012.3' ], 86 | 87 | [ ['-1.2'], '-1.2' ], 88 | [ ['-12.3'], '-12.3' ], 89 | [ ['-123.4'], '-123.4' ], 90 | [ ['-1234.5'], '-1,234.5' ], 91 | [ ['-12345.6'], '-12,345.6' ], 92 | [ ['-123456.7'], '-123,456.7' ], 93 | [ ['-1234567.8'], '-1,234,567.8' ], 94 | [ ['-12345678.9'], '-12,345,678.9' ], 95 | [ ['-123456789.0'], '-123,456,789.0' ], 96 | [ ['-1234567890.1'], '-1,234,567,890.1' ], 97 | [ ['-12345678901.2'], '-12,345,678,901.2' ], 98 | [ ['-123456789012.3'], '-123,456,789,012.3' ], 99 | 100 | [ [ 1.2 ], '1.2' ], 101 | [ [ 12.3 ], '12.3' ], 102 | [ [ 123.4 ], '123.4' ], 103 | [ [ 1234.5 ], '1,234.5' ], 104 | [ [ 12345.6 ], '12,345.6' ], 105 | [ [ 123456.7 ], '123,456.7' ], 106 | [ [ 1234567.8 ], '1,234,567.8' ], 107 | [ [ 12345678.9 ], '12,345,678.9' ], 108 | [ [ 123456789.1 ], '123,456,789.1' ], 109 | [ [ 1234567890.1 ], '1,234,567,890.1' ], 110 | [ [ 12345678901.2 ], '12,345,678,901.2' ], 111 | [ [ 123456789012.3 ], '123,456,789,012.3' ], 112 | 113 | [ [ -1.2 ], '-1.2' ], 114 | [ [ -12.3 ], '-12.3' ], 115 | [ [ -123.4 ], '-123.4' ], 116 | [ [ -1234.5 ], '-1,234.5' ], 117 | [ [ -12345.6 ], '-12,345.6' ], 118 | [ [ -123456.7 ], '-123,456.7' ], 119 | [ [ -1234567.8 ], '-1,234,567.8' ], 120 | [ [ -12345678.9 ], '-12,345,678.9' ], 121 | [ [ -123456789.1 ], '-123,456,789.1' ], 122 | [ [ -1234567890.1 ], '-1,234,567,890.1' ], 123 | [ [ -12345678901.2 ], '-12,345,678,901.2' ], 124 | [ [ -123456789012.3 ], '-123,456,789,012.3' ], 125 | 126 | ] 127 | 128 | Benchmark.options.initCount = 10 129 | Benchmark.options.minSamples = 20 130 | 131 | function run(fn, input) { 132 | return function() { fn.apply(fn, input) } 133 | } 134 | 135 | for (var i = 0, end = inputs.length; i < end; i++) { 136 | var input, name, rowResult 137 | 138 | input = inputs[i][0] 139 | answer = inputs[i][1] 140 | 141 | name = pad(20, inspect(input).slice(2, -2)) 142 | 143 | rowResult = { 144 | name: name, 145 | old: { info: null, valid: false }, 146 | new: { info: null, valid: false } 147 | } 148 | 149 | // start out with an object containing the info. 150 | // the onCycle event will replace it with a data array 151 | results.push(rowResult) 152 | 153 | // test if function produces answer. 154 | if (methods[0].apply(methods[0], input) === answer) { 155 | rowResult.old.valid = true 156 | suite.add(name, run(methods[0], input)) 157 | } 158 | 159 | if (methods[1].apply(methods[1], input) === answer) { 160 | rowResult.new.valid = true 161 | suite.add(name, run(methods[1], input)) 162 | } 163 | 164 | // if they're both invalid then we have to handle it right now 165 | if (rowResult.old.valid === false && rowResult.new.valid === false) { 166 | results[results.length - 1] = resultArray(rowResult) 167 | } 168 | } 169 | 170 | function filterInfo(info) { 171 | return { hz: info.hz, rme: info.stats.rme } 172 | } 173 | 174 | function formatOps(info) { 175 | var fixed, formatted 176 | fixed = info.hz.toFixed(0) 177 | formatted = revised(fixed) 178 | return pad(12, formatted) 179 | } 180 | 181 | function formatDelta(info) { 182 | var fixed, decorated 183 | fixed = info.rme.toFixed(1) 184 | decorated = '+-' + fixed + '%' 185 | return pad(6, decorated) 186 | } 187 | 188 | function formatDiff(oldResult, newResult) { 189 | var diff, percent, decorated 190 | if (oldResult.valid && newResult.valid) { 191 | diff = (newResult.info.hz - oldResult.info.hz) 192 | percent = (diff / oldResult.info.hz) * 100 193 | decorated = percent.toFixed(0) + '%' 194 | return pad(5, decorated) 195 | } else { 196 | return NA 197 | } 198 | } 199 | 200 | function resultArray(result) { 201 | var array, newIndex 202 | 203 | if (useDelta) { 204 | array = [ result.name, null, null, null, null, null ] 205 | 206 | array[2] = (result.old.valid) ? formatDelta(result.old.info) : NA 207 | array[4] = (result.new.valid) ? formatDelta(result.new.info) : NA 208 | 209 | newIndex = 3 210 | } else { 211 | array = [ result.name, null, null, null ] 212 | newIndex = 2 213 | } 214 | 215 | array[1] = (result.old.valid) ? formatOps(result.old.info) : INVALID 216 | array[newIndex] = (result.new.valid) ? formatOps(result.new.info) : INVALID 217 | array[array.length - 1] = formatDiff(result.old, result.new) 218 | 219 | return array 220 | } 221 | 222 | suite.on('cycle', function(event) { 223 | var it, result, which 224 | 225 | it = event.target 226 | result = results[row] 227 | 228 | // if all methods produced invalid results then this `result` will already be 229 | // an array. so, skip until we find a result row which is *not* an array. 230 | // that's the one which corresponds to the current cycle result. 231 | while (Array.isArray(result)) { 232 | row++ 233 | result = results[row] 234 | } 235 | 236 | if (result.old == null) 237 | console.log('result is missing `old`:',result) 238 | 239 | which = (result.old.info == null && result.old.valid) ? 'old' : 'new' 240 | 241 | console.log(which, 'completed', it.name) 242 | 243 | if (which === 'new') { 244 | result.new.info = filterInfo(it) 245 | results[row] = resultArray(result) 246 | row++ 247 | } else { 248 | result.old.info = filterInfo(it) 249 | } 250 | }) 251 | 252 | suite.on('complete', function() { 253 | var headers 254 | headers = [' input', ' old ', ' new ', '+'] 255 | if (useDelta) { 256 | headers.splice(2, 0, 'delta1') 257 | headers.splice(4, 0, 'delta2') 258 | } 259 | console.log() 260 | console.table(headers, results) 261 | }) 262 | 263 | console.log('comma-number benchmark (' + process.pid + ')') 264 | 265 | const ask = require('readline').createInterface({ 266 | input : process.stdin, 267 | output: process.stdout 268 | }) 269 | 270 | ask.question('Begin benchmark? (y/N) ', function(answer) { 271 | 272 | ask.close() 273 | 274 | if ((answer != null) && (answer[0] === 'y' || answer[0] === 'Y')) { 275 | suite.run({ 276 | async: false 277 | }) 278 | } 279 | 280 | else { 281 | console.log('quitting') 282 | } 283 | 284 | }) 285 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var tap = require('tap') 2 | var commaNumber = require('./index.js') 3 | 4 | function testEach(pairs, t, format) { 5 | t.plan(pairs.length) 6 | pairs.forEach(function (pair) { 7 | var input, actual, expected, inputString, description 8 | 9 | input = pair[0] 10 | actual = format(input) 11 | expected = pair[1] 12 | inputString = 13 | ('object' === typeof input) 14 | ? JSON.stringify(input) 15 | : ('string' === typeof input) 16 | ? '\'' + input + '\'' 17 | : input 18 | description = inputString + ' => ' + expected 19 | 20 | t.equal(actual, expected, description) 21 | }) 22 | } 23 | 24 | tap.test('Formatting', function (t) { 25 | 26 | var testPairs 27 | 28 | testPairs = [ 29 | // Positive numbers 30 | [0, '0'], 31 | [1, '1'], 32 | [12, '12'], 33 | [123, '123'], 34 | [1234, '1,234'], 35 | [12345, '12,345'], 36 | [123456, '123,456'], 37 | [1234567, '1,234,567'], 38 | [12345678, '12,345,678'], 39 | [123456789, '123,456,789'], 40 | [1234567890, '1,234,567,890'], 41 | [12345678901, '12,345,678,901'], 42 | [123456789012, '123,456,789,012'], 43 | [1234567890123, '1,234,567,890,123'], 44 | [Infinity, 'Infinity'], 45 | 46 | // With decimals 47 | [.1, '0.1'], 48 | [.12, '0.12'], 49 | [.123, '0.123'], 50 | [1.2, '1.2'], 51 | [1.23, '1.23'], 52 | [1.234, '1.234'], 53 | [12.3, '12.3'], 54 | [12.34, '12.34'], 55 | [123.4, '123.4'], 56 | [123.45, '123.45'], 57 | [1234.5, '1,234.5'], 58 | [1234.56, '1,234.56'], 59 | [12345.6, '12,345.6'], 60 | [12345.67, '12,345.67'], 61 | [123456.7, '123,456.7'], 62 | [123456.78, '123,456.78'], 63 | [123456.789, '123,456.789'], 64 | [1234567.8, '1,234,567.8'], 65 | [1234567.89, '1,234,567.89'], 66 | [1234567.899, '1,234,567.899'], 67 | [12345678.9, '12,345,678.9'], 68 | [12345678.99, '12,345,678.99'], 69 | [12345678.999, '12,345,678.999'], 70 | [123456789.1, '123,456,789.1'], 71 | [123456789.12, '123,456,789.12'], 72 | [123456789.123, '123,456,789.123'], 73 | [1234567890.1, '1,234,567,890.1'], 74 | [1234567890.12, '1,234,567,890.12'], 75 | [1234567890.123, '1,234,567,890.123'], 76 | [12345678901.2, '12,345,678,901.2'], 77 | [12345678901.23, '12,345,678,901.23'], 78 | [12345678901.234, '12,345,678,901.234'], 79 | [123456789012.3, '123,456,789,012.3'], 80 | [123456789012.34, '123,456,789,012.34'], 81 | [123456789012.345, '123,456,789,012.345'], 82 | [1234567890123.4, '1,234,567,890,123.4'], 83 | [1234567890123.45, '1,234,567,890,123.45'], 84 | [1234567890123.456, '1,234,567,890,123.456'], 85 | 86 | // Negative numbers 87 | [-1, '-1'], 88 | [-12, '-12'], 89 | [-123, '-123'], 90 | [-1234, '-1,234'], 91 | [-12345, '-12,345'], 92 | [-123456, '-123,456'], 93 | [-1234567, '-1,234,567'], 94 | [-12345678, '-12,345,678'], 95 | [-123456789, '-123,456,789'], 96 | [-1234567890, '-1,234,567,890'], 97 | [-12345678901, '-12,345,678,901'], 98 | [-123456789012, '-123,456,789,012'], 99 | [-1234567890123, '-1,234,567,890,123'], 100 | [-Infinity, '-Infinity'], 101 | 102 | // With decimals 103 | [-.1, '-0.1'], 104 | [-.12, '-0.12'], 105 | [-.123, '-0.123'], 106 | [-1.2, '-1.2'], 107 | [-1.23, '-1.23'], 108 | [-1.234, '-1.234'], 109 | [-12.3, '-12.3'], 110 | [-12.34, '-12.34'], 111 | [-123.4, '-123.4'], 112 | [-123.45, '-123.45'], 113 | [-1234.5, '-1,234.5'], 114 | [-1234.56, '-1,234.56'], 115 | [-12345.6, '-12,345.6'], 116 | [-12345.67, '-12,345.67'], 117 | [-123456.7, '-123,456.7'], 118 | [-123456.78, '-123,456.78'], 119 | [-123456.789, '-123,456.789'], 120 | [-1234567.8, '-1,234,567.8'], 121 | [-1234567.89, '-1,234,567.89'], 122 | [-1234567.899, '-1,234,567.899'], 123 | [-12345678.9, '-12,345,678.9'], 124 | [-12345678.99, '-12,345,678.99'], 125 | [-12345678.999, '-12,345,678.999'], 126 | [-123456789.1, '-123,456,789.1'], 127 | [-123456789.12, '-123,456,789.12'], 128 | [-123456789.123, '-123,456,789.123'], 129 | [-1234567890.1, '-1,234,567,890.1'], 130 | [-1234567890.12, '-1,234,567,890.12'], 131 | [-1234567890.123, '-1,234,567,890.123'], 132 | [-12345678901.2, '-12,345,678,901.2'], 133 | [-12345678901.23, '-12,345,678,901.23'], 134 | [-12345678901.234, '-12,345,678,901.234'], 135 | [-123456789012.3, '-123,456,789,012.3'], 136 | [-123456789012.34, '-123,456,789,012.34'], 137 | [-123456789012.345, '-123,456,789,012.345'], 138 | [-1234567890123.4, '-1,234,567,890,123.4'], 139 | [-1234567890123.45, '-1,234,567,890,123.45'], 140 | [-1234567890123.456, '-1,234,567,890,123.456'], 141 | 142 | // Strings 143 | ['0', '0'], 144 | ['1', '1'], 145 | ['12', '12'], 146 | ['123', '123'], 147 | ['1234', '1,234'], 148 | ['12345', '12,345'], 149 | ['123456', '123,456'], 150 | ['1234567', '1,234,567'], 151 | ['12345678', '12,345,678'], 152 | ['123456789', '123,456,789'], 153 | ['1234567890', '1,234,567,890'], 154 | ['12345678901', '12,345,678,901'], 155 | ['123456789012', '123,456,789,012'], 156 | ['1234567890123', '1,234,567,890,123'], 157 | 158 | // With decimals 159 | ['.1', '.1'], 160 | ['0.1', '0.1'], 161 | ['.12', '.12'], 162 | ['0.12', '0.12'], 163 | ['.123', '.123'], 164 | ['0.123', '0.123'], 165 | ['1.2', '1.2'], 166 | ['1.23', '1.23'], 167 | ['1.234', '1.234'], 168 | ['12.3', '12.3'], 169 | ['12.34', '12.34'], 170 | ['123.4', '123.4'], 171 | ['123.45', '123.45'], 172 | ['1234.5', '1,234.5'], 173 | ['1234.56', '1,234.56'], 174 | ['12345.6', '12,345.6'], 175 | ['12345.67', '12,345.67'], 176 | ['123456.7', '123,456.7'], 177 | ['123456.78', '123,456.78'], 178 | ['123456.789', '123,456.789'], 179 | ['1234567.8', '1,234,567.8'], 180 | ['1234567.89', '1,234,567.89'], 181 | ['1234567.899', '1,234,567.899'], 182 | ['12345678.9', '12,345,678.9'], 183 | ['12345678.99', '12,345,678.99'], 184 | ['12345678.999', '12,345,678.999'], 185 | ['123456789.1', '123,456,789.1'], 186 | ['123456789.12', '123,456,789.12'], 187 | ['123456789.123', '123,456,789.123'], 188 | ['1234567890.1', '1,234,567,890.1'], 189 | ['1234567890.12', '1,234,567,890.12'], 190 | ['1234567890.123', '1,234,567,890.123'], 191 | ['12345678901.2', '12,345,678,901.2'], 192 | ['12345678901.23', '12,345,678,901.23'], 193 | ['12345678901.234', '12,345,678,901.234'], 194 | ['123456789012.3', '123,456,789,012.3'], 195 | ['123456789012.34', '123,456,789,012.34'], 196 | ['123456789012.345', '123,456,789,012.345'], 197 | ['1234567890123.4', '1,234,567,890,123.4'], 198 | ['1234567890123.45', '1,234,567,890,123.45'], 199 | ['1234567890123.456', '1,234,567,890,123.456'], 200 | 201 | // Negative numbers 202 | ['-1', '-1'], 203 | ['-12', '-12'], 204 | ['-123', '-123'], 205 | ['-1234', '-1,234'], 206 | ['-12345', '-12,345'], 207 | ['-123456', '-123,456'], 208 | ['-1234567', '-1,234,567'], 209 | ['-12345678', '-12,345,678'], 210 | ['-123456789', '-123,456,789'], 211 | ['-1234567890', '-1,234,567,890'], 212 | ['-12345678901', '-12,345,678,901'], 213 | ['-123456789012', '-123,456,789,012'], 214 | ['-1234567890123', '-1,234,567,890,123'], 215 | [-Infinity, '-Infinity'], 216 | 217 | // With decimals 218 | ['-.1', '-.1'], 219 | ['-.12', '-.12'], 220 | ['-.123', '-.123'], 221 | ['-1.2', '-1.2'], 222 | ['-1.23', '-1.23'], 223 | ['-1.234', '-1.234'], 224 | ['-12.3', '-12.3'], 225 | ['-12.34', '-12.34'], 226 | ['-123.4', '-123.4'], 227 | ['-123.45', '-123.45'], 228 | ['-1234.5', '-1,234.5'], 229 | ['-1234.56', '-1,234.56'], 230 | ['-12345.6', '-12,345.6'], 231 | ['-12345.67', '-12,345.67'], 232 | ['-123456.7', '-123,456.7'], 233 | ['-123456.78', '-123,456.78'], 234 | ['-123456.789', '-123,456.789'], 235 | ['-1234567.8', '-1,234,567.8'], 236 | ['-1234567.89', '-1,234,567.89'], 237 | ['-1234567.899', '-1,234,567.899'], 238 | ['-12345678.9', '-12,345,678.9'], 239 | ['-12345678.99', '-12,345,678.99'], 240 | ['-12345678.999', '-12,345,678.999'], 241 | ['-123456789.1', '-123,456,789.1'], 242 | ['-123456789.12', '-123,456,789.12'], 243 | ['-123456789.123', '-123,456,789.123'], 244 | ['-1234567890.1', '-1,234,567,890.1'], 245 | ['-1234567890.12', '-1,234,567,890.12'], 246 | ['-1234567890.123', '-1,234,567,890.123'], 247 | ['-12345678901.2', '-12,345,678,901.2'], 248 | ['-12345678901.23', '-12,345,678,901.23'], 249 | ['-12345678901.234', '-12,345,678,901.234'], 250 | ['-123456789012.3', '-123,456,789,012.3'], 251 | ['-123456789012.34', '-123,456,789,012.34'], 252 | ['-123456789012.345', '-123,456,789,012.345'], 253 | ['-1234567890123.4', '-1,234,567,890,123.4'], 254 | ['-1234567890123.45', '-1,234,567,890,123.45'], 255 | ['-1234567890123.456', '-1,234,567,890,123.456'], 256 | 257 | ] 258 | 259 | testEach(testPairs, t, commaNumber) 260 | // t.plan(testPairs.length) 261 | // testPairs.forEach(function (pair) { 262 | // var input, actual, expected, inputString, description 263 | // 264 | // input = pair[0] 265 | // actual = format(input) 266 | // expected = pair[1] 267 | // inputString = 268 | // ('object' === typeof input) 269 | // ? JSON.stringify(input) 270 | // : ('string' === typeof input) 271 | // ? '\'' + input + '\'' 272 | // : input 273 | // description = inputString + ' => ' + expected 274 | // 275 | // t.equal(actual, expected, description) 276 | // }) 277 | }) 278 | 279 | tap.test('Invalid input', function (t) { 280 | 281 | var input 282 | 283 | input = [ 284 | [], 285 | {}, 286 | null, 287 | undefined, 288 | 'abc', 289 | ] 290 | 291 | t.plan(6) 292 | t.equal(commaNumber(input[0]), input[0], '[] => []') 293 | t.equal(commaNumber(input[1]), input[1], '{} => {}') 294 | t.equal(commaNumber(input[2]), input[2], 'null => null') 295 | t.equal(commaNumber(input[3]), input[3], 'undefined => undefined') 296 | t.equal(commaNumber(input[4]), input[4], '\'abc\' => \'abc\'') 297 | 298 | t.equal(isNaN(commaNumber(NaN)), true, 'NaN => NaN') 299 | }) 300 | 301 | tap.test('Separator with string inputs', function (t) { 302 | t.plan(6) 303 | t.equal(commaNumber('1000', ' '), '1 000', '1000 => 1 000') 304 | t.equal(commaNumber('1000', '.'), '1.000', '1000 => 1.000') 305 | t.equal(commaNumber('-1000', '.'), '-1.000', '-1000 => -1.000') 306 | t.equal(commaNumber('1000.12', ' '), '1 000.12', '1000.12 => 1 000.12') 307 | t.equal(commaNumber('1000.12', '.'), '1.000.12', '1000.12 => 1.000.12') 308 | t.equal(commaNumber('-1000.12', '.'), '-1.000.12', '-1000.12 => -1.000.12') 309 | }) 310 | 311 | tap.test('Separator with number inputs', function (t) { 312 | t.plan(6) 313 | t.equal(commaNumber(1000, ' '), '1 000', '1000 => 1 000') 314 | t.equal(commaNumber(1000, '.'), '1.000', '1000 => 1.000') 315 | t.equal(commaNumber(-1000, '.'), '-1.000', '-1000 => -1.000') 316 | t.equal(commaNumber(1000.12, ' '), '1 000.12', '1000.12 => 1 000.12') 317 | t.equal(commaNumber(1000.12, '.'), '1.000.12', '1000.12 => 1.000.12') 318 | t.equal(commaNumber(-1000.12, '.'), '-1.000.12', '-1000.12 => -1.000.12') 319 | }) 320 | 321 | tap.test('Decimal Separator with string inputs', function (t) { 322 | t.plan(6) 323 | t.equal(commaNumber('1234.5', undefined, '.'), '1,234.5', '1234.5 => 1,234.5') 324 | t.equal(commaNumber('1234,5', '.', ','), '1.234,5', '1234,5 => 1.234,5') 325 | t.equal(commaNumber('1234 5', undefined, ' '), '1,234 5', '1234 5 => 1,234 5') 326 | t.equal(commaNumber('-1234.5', undefined, '.'), '-1,234.5', '-1234.5 => -1,234.5') 327 | t.equal(commaNumber('-1234,5', '.', ','), '-1.234,5', '-1234,5 => -1.234,5') 328 | t.equal(commaNumber('-1234 5', undefined, ' '), '-1,234 5', '-1234 5 => -1,234 5') 329 | }) 330 | 331 | tap.test('Decimal Separator with number inputs', function (t) { 332 | t.plan(6) 333 | t.equal(commaNumber(1234.5, undefined, '.'), '1,234.5', '1234.5 => 1,234.5') 334 | t.equal(commaNumber(1234.5, '.', ','), '1.234,5', '1234,5 => 1.234,5') 335 | t.equal(commaNumber(1234.5, undefined, ' '), '1,234 5', '1234 5 => 1,234 5') 336 | t.equal(commaNumber(-1234.5, undefined, '.'), '-1,234.5', '-1234.5 => -1,234.5') 337 | t.equal(commaNumber(-1234.5, '.', ','), '-1.234,5', '-1234,5 => -1.234,5') 338 | t.equal(commaNumber(-1234.5, undefined, ' '), '-1,234 5', '-1234 5 => -1,234 5') 339 | }) 340 | 341 | tap.test('bindWith() and string inputs', function (t) { 342 | 343 | var boundVersion, testPairs 344 | 345 | boundVersion = commaNumber.bindWith('_', '!') 346 | 347 | testPairs = [ 348 | ['1234!56', '1_234!56'], 349 | ['1234567!89', '1_234_567!89'], 350 | ['1234567890!12', '1_234_567_890!12'], 351 | ['-1234!56', '-1_234!56'], 352 | ['-1234567!89', '-1_234_567!89'], 353 | ['-1234567890!12', '-1_234_567_890!12'], 354 | ] 355 | 356 | testEach(testPairs, t, boundVersion) 357 | }) 358 | 359 | tap.test('bindWith() and number inputs', function (t) { 360 | 361 | var boundVersion, testPairs 362 | 363 | boundVersion = commaNumber.bindWith('_', '!') 364 | 365 | testPairs = [ 366 | [1234.56, '1_234!56'], 367 | [1234567.89, '1_234_567!89'], 368 | [1234567890.12, '1_234_567_890!12'], 369 | [-1234.56, '-1_234!56'], 370 | [-1234567.89, '-1_234_567!89'], 371 | [-1234567890.12, '-1_234_567_890!12'], 372 | ] 373 | 374 | testEach(testPairs, t, boundVersion) 375 | }) 376 | --------------------------------------------------------------------------------