Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
shrpne committed Jul 30, 2018
0 parents commit 0a1bc33
Show file tree
Hide file tree
Showing 18 changed files with 456 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["env"]
}
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[{.babelrc,.stylelintrc,.eslintrc*,jest.config,*.json,*.jsb3,*.jsb2,*.bowerrc,*.yaml,*.yml}]
indent_size = 2
51 changes: 51 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// http://eslint.org/docs/user-guide/configuring

module.exports = {
root: true,
// parser: 'babel-eslint',
// parserOptions: {
// sourceType: 'module'
// },
env: {
browser: true,
jest: true,
},
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
extends: 'airbnb-base',
// required to lint *.vue files
// plugins: [
// 'html'
// ],
// // add your custom rules here
rules: {
'indent': ["error", 4],
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': 0,
// disable length limit
'max-len': 0,
// allow `new Buffer()`
'no-buffer-constructor': 0,
// allow assigning to function parameter
'no-param-reassign': 0,
'no-underscore-dangle': 0,
'no-else-return': 0,
"no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }],
'no-use-before-define' : 0,
'object-curly-newline': 0,
'import/prefer-default-export': 1,
},
overrides: [
{
files: ['examples/*'],
rules: {
"import/no-extraneous-dependencies": 0,
'no-console': 0,
}
}
]
};
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@


# dependencies
node_modules
package-lock.json

/coverage/

# logs
npm-debug.log
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
language: node_js
node_js:
- stable
- lts/*
jobs:
include:
- stage: Produce Coverage
node_js: stable
script: jest --coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Copyright (c) 2018, Respective Authors all rights reserved.

The MIT License

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:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

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.
13 changes: 13 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = {
moduleNameMapper: {
'~(.*)$': '<rootDir>/$1',
},
transform: {
'^.+\\.jsx?$': 'babel-jest',
},
transformIgnorePatterns: [
'node_modules/(?!(minterjs-util|other-module)/)',
],
/** fix jest bug @see https://github.com/facebook/jest/issues/6766 */
testURL: 'http://localhost'
};
44 changes: 44 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "pretty-num",
"version": "0.0.1",
"description": "Lightweight module for formatting numbers to a human readable string",
"main": "src/index.js",
"files": [
"/src/"
],
"scripts": {
"prepublishOnly": "npm run lint && npm run test",
"lint": "eslint --ext .js ./src ./test",
"lint:fix": "eslint --ext .js ./src ./test --fix",
"test": "jest",
"coverage": "jest --coverage"
},
"repository": {
"type": "git",
"url": "git+https://github.com/shrpne/pretty-num.git"
},
"keywords": [
"format",
"pretty",
"number",
"human",
"readable",
"string",
"text"
],
"author": "shrpne <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/shrpne/pretty-num/issues"
},
"dependencies": {},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-jest": "^23.4.0",
"babel-preset-env": "^1.7.0",
"eslint": "^5.2.0",
"eslint-config-airbnb-base": "^13.0.0",
"eslint-plugin-import": "^2.13.0",
"jest": "^23.4.1"
}
}
80 changes: 80 additions & 0 deletions src/from-exponential.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
*
* @param {number|string|Array} num - number or array of its parts
* @return {string}
*/

export default function fromExponential(num) {
const eParts = getExponentialParts(num);
if (!isExponential(eParts)) {
throw new Error('Only exponential numbers can be converted properly');
}

const sign = eParts[0][0] === '-' ? '-' : '';
const digits = eParts[0].replace(/^-/, '');
const digitsParts = digits.split('.');
const wholeDigits = digitsParts[0];
const fractionDigits = digitsParts[1] || '';
let e = Number(eParts[1]);

if (e === 0) {
return `${sign + wholeDigits}.${fractionDigits}`;
} else if (e < 0) {
// move dot to the left
const countWholeAfterTransform = wholeDigits.length + e;
if (countWholeAfterTransform > 0) {
// transform whole to fraction
const wholeDigitsAfterTransform = wholeDigits.substr(0, countWholeAfterTransform);
const wholeDigitsTransformedToFracton = wholeDigits.substr(countWholeAfterTransform);
return `${sign + wholeDigitsAfterTransform}.${wholeDigitsTransformedToFracton}${fractionDigits}`;
} else {
// not enough whole digits: prepend with fractional zeros

// first e goes to dotted zero
let zeros = '0.';
e += 1;
while (e) {
zeros += '0';
e += 1;
}
return sign + zeros + wholeDigits + fractionDigits;
}
} else {
// move dot to the right
const countFractionAfterTransform = fractionDigits.length - e;
if (countFractionAfterTransform > 0) {
// transform fraction to whole
// countTransformedFractionToWhole = e
const fractionDigitsAfterTransform = fractionDigits.substr(e);
const fractionDigitsTransformedToWhole = fractionDigits.substr(0, e);
return `${sign + wholeDigits + fractionDigitsTransformedToWhole}.${fractionDigitsAfterTransform}`;
} else {
// not enough fractions: append whole zeros
let zerosCount = -countFractionAfterTransform;
let zeros = '';
while (zerosCount) {
zeros += '0';
zerosCount -= 1;
}
return sign + wholeDigits + fractionDigits + zeros;
}
}
}

/**
* Return two parts array of exponential number
* @param {number|string|Array} num
* @return {string[]}
*/
export function getExponentialParts(num) {
return Array.isArray(num) ? num : String(num).split(/[eE]/);
}

/**
*
* @param {number|string|Array} num - number or array of its parts
*/
export function isExponential(num) {
const eParts = getExponentialParts(num);
return !Number.isNaN(Number(eParts[1]));
}
19 changes: 19 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import stripZeros from './strip-zeros';
import reducePrecision from './reduce-precision';
import fromExponential from './from-exponential';

/**
* @param {number|string} num
* @param {number} [precision]
* @return {string}
*/
export default function prettyNum(num, precision) {
num = stripZeros(num);
const eParts = String(num).split(/[eE]/);

if (eParts.length === 2) {
num = fromExponential(num);
}

return reducePrecision(num, precision);
}
26 changes: 26 additions & 0 deletions src/reduce-precision.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @param {string|number} num
* @param {number} precision
* @return {string}
*/
export default function reducePrecision(num, precision) {
const numString = num.toString();

// decimal exponential number
if ((/e-/i).test(numString)) {
return num.toPrecision(precision);
}
// integer exponential number
if ((/e/i).test(numString)) {
return numString;
}


const notMeaningfulFraction = numString.match(/\.0*/);
if (notMeaningfulFraction) {
const MeaningfulFractionIndex = notMeaningfulFraction.index + notMeaningfulFraction[0].length;
return numString.substr(0, MeaningfulFractionIndex + precision);
}

return numString;
}
13 changes: 13 additions & 0 deletions src/round.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Round `value` to `fractionDigitNumber` digits after dot
* @param {number} value
* @param {number} fractionDigitNumber - number of fraction digits after dot
* @return {number}
*/
export default function round(value, fractionDigitNumber) {
if (fractionDigitNumber < 0) {
throw new Error('Number of fraction digits should be positive');
}
const tenPower = 10 ** fractionDigitNumber;
return Math.round(value * tenPower) / tenPower;
}
16 changes: 16 additions & 0 deletions src/strip-zeros.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Strip unnecessary last zeros after dot
* @param num
* @return {string|number}
*/
export default function stripZeros(num) {
if (typeof num === 'string') {
// strip ending zeros
if (num.indexOf('.') !== -1) {
num = num.replace(/\.?0+$/, '');
}
// strip leading zeros
num = num.replace(/^0+(?!\.)(?!$)/, '');
}
return num;
}
49 changes: 49 additions & 0 deletions test/from-exponential.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import fromExponential from '../src/from-exponential';

describe('prettyNum', () => {
test('positive exponential', () => {
expect(fromExponential(0.123e-10)).toEqual('0.0000000000123');
expect(fromExponential(1.123e-10)).toEqual('0.0000000001123');
expect(fromExponential(12.123e-10)).toEqual('0.0000000012123');
expect(fromExponential(123.123e-10)).toEqual('0.0000000123123');
expect(fromExponential(123.123e+20)).toEqual('12312300000000000000000');
expect(Number.MAX_VALUE).toEqual(1.7976931348623157e+308);
expect(fromExponential(Number.MAX_VALUE)).toEqual('179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000');
expect(Number.MIN_VALUE).toEqual(5e-324);
expect(fromExponential(Number.MIN_VALUE)).toEqual('0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005');
});

test('negative exponential', () => {
expect(fromExponential(-0.123e-10)).toEqual('-0.0000000000123');
expect(fromExponential(-1.123e-10)).toEqual('-0.0000000001123');
expect(fromExponential(-12.123e-10)).toEqual('-0.0000000012123');
expect(fromExponential(-123.123e-10)).toEqual('-0.0000000123123');
expect(fromExponential(-123.123e+20)).toEqual('-12312300000000000000000');
});

test('positive exponential-like strings', () => {
expect(fromExponential('0.123e-1')).toEqual('0.0123');
expect(fromExponential('1.123e-1')).toEqual('0.1123');
expect(fromExponential('12.123e-1')).toEqual('1.2123');
expect(fromExponential('123.123e-1')).toEqual('12.3123');
expect(fromExponential('123.123e+4')).toEqual('1231230');
expect(fromExponential('123.123e+0')).toEqual('123.123');
});

test('negative exponential-like strings', () => {
expect(fromExponential('-0.123e-1')).toEqual('-0.0123');
expect(fromExponential('-1.123e-1')).toEqual('-0.1123');
expect(fromExponential('-12.123e-1')).toEqual('-1.2123');
expect(fromExponential('-123.123e-1')).toEqual('-12.3123');
expect(fromExponential('-123.123e+4')).toEqual('-1231230');
expect(fromExponential('-123.123e+0')).toEqual('-123.123');
});

test('not exponential', () => {
expect(() => fromExponential(0)).toThrow();
expect(() => fromExponential(0.0012, 3)).toThrow();
expect(() => fromExponential(123)).toThrow();
expect(() => fromExponential(123e+14)).toThrow(); // 12300000000000000 is not exponential
expect(() => fromExponential(0.123e-4)).toThrow(); // 0.0000123 is not exponential
});
});
14 changes: 14 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import prettyNum from '../src/index';

describe('prettyNum', () => {
test('exponential', () => {
expect(prettyNum(1.123e-10, 3)).toEqual('0.000000000112');
expect(prettyNum(12.123e-10, 3)).toEqual('0.00000000121');
expect(prettyNum(123.123e-10, 3)).toEqual('0.0000000123');
expect(prettyNum(123.123e+4, 3)).toEqual('1231230');
});

test('exponential', () => {
expect(prettyNum(0.00123456, 3)).toEqual('0.00123');
});
});
Loading

0 comments on commit 0a1bc33

Please sign in to comment.