@oliasoft-open-source/units
v4.2.2
Published
Package for units management
Downloads
4,273
Readme
Oliasoft Unit Handling
Table of Contents
Introduction
Hi, and welcome to Oliasoft's Unit handling repository! Oliasoft is using this to convert numbers between different units in all of our products. As you probably know we are doing various physics calculation that relies on precise definitions for all input data. We need to know the valid range and precision of a value as well as what unit the given value represent. Before doing calculations we therefor convert all numbers to proper JavaScript numbers in a given known unit used in the calculations. We refer to these values as calculation units. The users of our products are however allowed to input values, either through the GUI or directly using the APIs, in any unit they prefer. We refer to these values as input units. We always store the input value together with its unit unconverted, hence input units is the same as the stored units. Finally, we offer the users to view all graphs and numbers in their preferred units. We refer these values as view units (GUI) or output units (API).
All values with numbers are stored as a string with the following special format;
'number|unit'
Number here is very flexible and all of the following is valid "number" strings:
- 2.13
- -2,13
- 4e-05
- 4E+3
- -120,000.02
- -20 1/2
I.e. it support both . and , as comma/thousands separator, exponents and fractions. Examples of different values can be seen in the units tests. By storing the inputted value without any conversions, we maintain valuable precision where needed and also provide the user with recognizable values. We use base units, as described in for example International System of Units (SI), for conversions. By converting all units to the base first, through intermediate convesions, we are able to keep the amount of conversion permutations to a minimum. The same unit can exists in multiple quantities and we allow custom quantity categories for application flexibility.
Install
npm i @oliasoft/units --save
Basic usage
Adding units to a number
As mentioned in the introduction this repository is working with numbers and units represented in a special string format. In order to get a string with number and unit use withUnit. Example:
const myValueWithUnit = withUnit(3.14, 'cm'); // '3.14|cm'
It's also possible to convert the number to a different unit before getting the string, by calling unumWithUnit. Given a string with unit you can use getValue and getUnit to get the unit part, or use split to get both as an array.
const myValue = getValue(myValueWithUnit); // '3.14'
const myUnit = getUnit(myValueWithUnit); // 'cm'
const [value, unit] = split(myValueWithUnit); // ['3.14, 'cm']
Never manipulate the strings directly in case we ever change the syntax!
Converting number to calculation units
Before doing any calculations with variables stored in our special string format, we need to convert the values from the stored units to the calculation units. Knowing the target unit we will use the convertAndGetValue method for converting and returning the number in the target calculation unit:
const value = convertAndGetValue(number, toUnit, fromUnit);
The first argument is the number with or without unit. The second parameter is the target unit, while the last parameter is an optional from unit that's used as a fallback if the first parameter is given without unit.
Examples:
const value = convertAndGetValue('10|m', 'm'); // 10
value = convertAndGetValue('10|m', 'in'); // 393.7007874015748
value = convertAndGetValue('10', 'in', 'm'); // 393.7007874015748
If you want to convert values without units directly to another unit, you can use the to method. Both pure numbers and string representing numbers are converted.
const convertedValue = to(10, 'm', 'in'); // 393.7007874015748
same as
const convertedValue = to('10', 'm', 'in'); // 393.7007874015748
Working with Quantities
Quantities are lists of units that represent the same measurement of a given quantity, for example length measured in m, in, ft etc. We have a few different methods for getting the units and quantities. One can use getQuantities to get a list of all the defined quantities or unit categories if you will. Given a quantity there's multiple methods to get units for that quantity. Use getUnitsForQuantity to get a pure list of units, or you can get a list of the objects following the AltUnitWithLabel
interface by calling getAltUnitsListByQuantity which will also return formatted labels for the given unit. For example, m³ for cubic meters. The same labels can also be looked up directly by calling label. To get the base unit for a given quantity one can use the method unitFromQuantity.
getQuantities(); // ['acceleration', 'angleGradient', 'angles', 'areaOther', ...]
getUnitsForQuantity('angle'); // ['deg', 'rad']
unitFromQuantity('force') // 'N'
Working with fractions
Since this library is representing numbers and units with strings, we also have the flexibility to work directly with fractions. Use fraction to convert a fraction, given as a string, to the corresponding numeric representation. Similar one can use asFraction to convert a decimal number to a fraction (if it exist). One can also use the method numFraction to convert from fraction to number, which will return the input if the conversion fails unlike fraction
which returns NaN
for failed conversions.
const half = asFraction('0.5') // '1/2';
const numeric = fraction(half) // 0.5;
fraction('13/0') // Infinity;
fraction('Garbage') // NaN;
numFraction('Garbage') // 'Garbage';
Input and conversions
Several methods exist for processing GUI inputs and making sure they are valid numbers. Raw inputs from the user can be fed through validateAndClean method, which returns a new valid number-unit string.
validateAndClean('123-', '1234-'); // '1234';
validateAndClean('123|m', '1234'); // '1234|m'
Other similar method is cleanNumStr and toNum which works directly with numbers or numbers given as string. For directly returning the number use cleanNum which is identical to [clenNumStr] except for the return value.
cleanNumStr('1000,000,000.1.1'); // '100000000011'
toNum(',1'); // 0.1
cleanNumStr(',1')); // '0.1';
cleanNum(',1')); // 0.1
For showing a list of all values in different units use altUnitsList which returns a list of the value in all units defined in the given quantity. In order to convert between units with similar precision one can use convertSamePrecision.
altUnitsList('180', 'deg'); // [['180', 'deg', '°'], ['3.14', 'rad', 'rad']]
Given a value-unit string we offer a few methods for query the nature of it. Use isEmptyValueWithUnit to check if the unit is un-initialized (empty) and related use isValueWithUnit to check if a string contains both a value and an "valid" unit. Valid here means an unit known to our repository. Use isNumeric to check if a string represents a valid javascript number.
isEmptyValueWithUnit('10|m'); // false
isEmptyValueWithUnit('|m'); // true
isValueWithUnit('10|m'); // true
isValueWithUnit('10'); // false
isNumeric(1e20); //true
isNumeric('1e20'); //true
isNumeric('e20'); // false
Formatting
For printing nice numbers in GUI and reports we offer different helpers. As already mentioned, one can get formatted unit labels, for example m³ for cubic meters, by calling label. Calling roundNumberWithLabel gives you a value, rounded to the wanted precision, with unit label. Use round to round just the value. Depending on what value that is stored in the string it can sometimes be hard to know what precision to use for display. The method getNumberOfDigitsToShow will analyse the input and suggest a reasonable precision for you.
const forPrint = roundNumberWithLabel('1000.1284325|kg/m3'); // '1000.13 kg/m³'
const noOfDigits = getNumberOfDigitsToShow(1e9); // 12
const outputValue = round(1e9, noOfDigits); // 0.0000000001
Working with Tables
Most of the methods in the library works with single value-unit strings and for tables we encourage you to store the value-unit string directly as keyed objects. Example:
const myTable = [
{ depth: '15|m', anotherDepth: '42|ft', od: '35|in', density: '42|kg/m3' },
{ depth: '16|m', anotherDepth: '43|ft', od: '36|in', density: '43|kg/m3' },
];
That said, we also have support for a special custom table format where the first row specifies the unit and the following rows stores pure numbers without unit.
Example:
const myExcelTable = [
['m', 'ft', 'in', 'kg/m3'],
[15, 42, 35, 42],
[16, 43, 36, 50]
];
This then represent the values 15 m, 42 ft, 35 in, 42 kg/m³, 16 m, 50 in, etc. This format is convenient for storing data imported/exported to i.e. Excel. With data in this shape you can call convertTable to convert all values in the table to different units.
convertTable(['cm', 'ft', 'cm', 'sg'], myExcelTable);
/*
[
['cm', 'ft', 'cm', 'sg'],
[1500, 42, 88.9, 0.042],
[1600, 43, 91.44, 0.05]
]
*/
Methods
withUnit(value, unit, defaultVal = '') {...}
Get a value-unit string, i.e. value with unit splitted by | separator
withUnit(1.123, 'm'); // '1.123|m'
withUnit(-10.314, 'K/100m'); // '-10.314|K/100m'
unumWithUnit(numWithUnit, toUnit, fromUnit?) {...}
Converts to given toUnit and return the converted value with unit
unumWithUnit(2.2, 'kg/m3', 'sg'); // '2200|kg/m3'
isEmptyValueWithUnit(val) {...}
Checks if input is a string that starts with '|', e.g. '|m' or '|in'
isEmptyValueWithUnit('|m'); // true
isEmptyValueWithUnit(NaN); // false
isEmptyValueWithUnit('m'); // false
isValueWithUnit(value) {...}
Takes user input and returns true
if is value with unit and false in every other case
isValueWithUnit('m'); // false
isValueWithUnit('5'); // false
isValueWithUnit(5); // false
isValueWithUnit('5|m'); // true
isNumeric(val) {...}
Check if provided argument is number
isNumeric(1e20); // true
isNumeric('1e20'); // true
isNumeric(NaN); // false
isNumeric(Infinity); // false
allNumbers(arr) {...}
Check array values if all are numbers
allNumbers([1, 2, 1.2, 5]); // true
allNumbers([1, 2, '1.2', 5]); // false
allNumbers([1, 1, 2, Infinity, 1.2]); // true
formatNumber(number) {...}
Outputs "pretty" number (with thousands separators), taken from this
formatNumber(1.1); // '1.1'
formatNumber(100000.123); // '100,000.123';
formatNumber('100000.123'); // '100,000.123';
charCount(chr, str)
Counts all occurence of given character
charCount('1', '1.1'); // 2;
charCount(1, '1.1'); // 2;
charCount(0, '100000.123'); // 5;
validateAndClean(previousValue, nextText) {...}
Validates and cleans raw text numeric user input, typically from user input. The previous value is for optionally determining the pre-existing unit. The next text is a raw input string. The return value is reformatted from the next text (removing invalid patterns)
validateAndClean('123', '1234'); // '1234'
validateAndClean('123e15', '123e154'); // '123e154'
validateAndClean('123.|m', '123.'); // '123.|m');
validateAndClean('-2e50|m', '-2e500'); // '-2e50|m'
validateAndClean('2|m', '2e-3'); // '2e-3|m'
getNumberOfDigitsToShow(num, maxNumDigits = 20) {...}
Calculates the number of digits to be rounded off, typically for values less than 1 E.g. when trying to format 1e-9 byroundNumber(), the output value will only show '0' if just rounded off with 4 digits fromroundNumber(val) Then it is more useful to calculate the number of digits to be rounded off, and pass this in, i.e.round(val, getNumberOfDigitsToShow(val)) which will return 0.0000000001.
getNumberOfDigitsToShow('1'); // 4
getNumberOfDigitsToShow(0.1); // 4
getNumberOfDigitsToShow(1.1); // 4
getNumberOfDigitsToShow(4.2e-8); // 11
getNumberOfDigitsToShow('100000.123'); // 4
getNumberOfDigitsToShow('100000.123', 3); // 3
getNumberOfDigitsToShow(0); // 4
round(num, round = 4)
Formating / rounding number provided in argument
round(1.11231231); // 1.1123
round('100000.123'); // '100000.123'
round('100000.123', 2); // '100000.12'
round('100000.123|m', 2); // '100000.12|m'
round(null, 2) // null
roundNumberWithLabel(value, roundTo = 2) {...}
Round input value and return with labeled unit
roundNumberWithLabel('1000.1284325|kg/m3'); // '1000.13 kg/m³'
roundNumberWithLabel('-999.999991|kg/m3', 5); // '-999.99999 kg/m³'
roundNumberWithLabel('-999.999999|kg/m3', 5); // '-1000 kg/m³'
fraction(str)
Convert set fraction to decimal value will return either number in decimal format, Infinity if fraction is divided by 0
fraction('1/3'); // 0.333
fraction('1/10'); // 0.1
fraction('13/0'); // Infinity
fraction('13e2b'); // NaN
fraction(null); // NaN
fraction(undefined); // NaN
fraction([1, 2]); // NaN
fraction(''); // NaN
unitFromQuantity(quantity) {...}
Get base unit from given quantity
unitFromQuantity('force'); // 'N';
unitFromQuantity('notsupported'); // undefined;
getAltUnitsListByQuantity(quantity) {...}
Get list of alternative units, with labels, for a given quantity.
getAltUnitsListByQuantity('angles'); // [{unit: 'deg', label: '°'}, {unit: 'rad', label: 'rad'}];
getAltUnitsListByQuantity('qwe123'); // undefined;
getUnitsForQuantity(quantity) {...}
Get list of units for a given quantity
getUnitsForQuantity('force'); // ['tonnes', 'lbf', 'kgf', 'N', 'kN', 'tonneForce', 'klbf']
getUnitsForQuantity('depth'); // ['m', 'ft']
getUnitsForQuantity('acceleration'); // ['ft/s2', 'm/s2'])
getUnitsForQuantity('angles');// ['deg', 'rad']);
getUnitsForQuantity('dls'); // ['deg/10m', 'deg/30m', 'deg/100ft'])
getUnitsForQuantity('shit'); // undefined
toBase(value, quantity) {...}
Convert value to the base unit given by the quantity
toBase('1|m', 'length'); // 1
toBase('1|cm', 'length'); // 0.01
toBase('1|tonnes', 'weight'); // 1000
altUnitsList(value, quantity, defaultUnit?) {...}
Get list of values, with same precision as the given value, in all the units of the given quantity
altUnitsList('10|m', 'length');
/* [['10', 'm', 'm'],
['32.8', 'ft', 'ft'],
['0.01', 'km', 'km'],
['394', 'in', 'in'],
['10000', 'mm', 'mm']]
*/
altUnitsList('180', 'deg');
/*
[['180', 'deg', '°'],
['3.14', 'rad', 'rad']]
*/
convertTable(toUnitRow, table, defaultUnitRow?, removeFinalUnitsRow=false) {...}
Convert table of values to another unit
const table = [
['m', 'ft', 'in', 'kg/m3'],
[15, 42, 35, 42],
[16, 43, 36, 50]];
convertTable(['cm', 'ft', 'cm', 'sg'], table);
/*
[
['cm', 'ft', 'cm', 'sg'],
[1500, 42, 88.9, 0.042],
[1600, 43, 91.44, 0.05]
]
*/
getQuantities() {...}
Get list of all defined quantities
getQuantities(); // ['acceleration', 'angleGradient', 'angles', 'areaOther', ...]
checkAndCleanDecimalComma(val) {...}
Find double dot and comma in value and replace it with decimal dot. For example: 123,4 => 123.4 or 123..4 => 123,4
checkAndCleanDecimalComma(123); // 123;
checkAndCleanDecimalComma('36,6'); // '36.6';
checkAndCleanDecimalComma('36..6'); // '36.6';
to(value, fromUnit, toUnit) {...}
Convert value to another unit
to(1, 'm', 'ft'); // 1 / 0.3048
to(0, 'C', 'F'); // 32
to(32, 'F', 'C'); // 0
to(50, '%', 'fr'); // 0.5
to(0.125, 'fr', '%'); // 12.5
to(180, 'deg', 'rad').toFixed(4); // '3.1416'
to(1, 'rad', 'deg').toFixed(4); // '57.2958'
to('1,12', 'rad', 'deg').toFixed(4); // '64.1713'
to('1....12', 'rad', 'deg').toFixed(4); // '64.1713'
to('1,,12', 'rad', 'deg').toFixed(4); // '64.1713'
to('1,,.12', 'rad', 'deg').toFixed(4); // '64.1713'
split(numWithUnit) {...}
Split string into value and unit.
split('-12,2m'); // ['-12.2', 'm'];
split('-12 1/2m'); // ['-12 1/2', 'm'];
getValue(numWithUnit) {...}
Get unit of the number with unit string ("1|m") will return "m"
getValue('12.2'); // '12.2'
getValue('12.2m'); // '12.2'
getValue('12.2|m'); // '12.2'
getValue('|m'); // '';
getValue('m'); // '';
getUnit(numWithUnit) {...}
Get unit of the number with unit string
getUnit('-2|in2'); // 'in2'
getUnit('12.2|m'); // 'm'
getUnit('12.2m'); // 'm';
getUnit('|m'); // 'm';
getUnit('12.2'); // '';
label(unitKey) {...}
Returns a print friendly unit representation
label('m3'); // 'm³'
label('1/bar'); // 'bar⁻¹'
convertAndGetValue(numWithUnit, toUnit, fromUnit?) {...}
Convert value with unit to another unit Will try to pick fromUnit
from numWithUnit
if it was not provided
convertAndGetValue('1 1/2', 'in', 'in'); // 1.5
convertAndGetValue('1 1/2 in', 'in'); // 1.5
convertAndGetValue('-1 1/2 in', 'in'); // -1.5
convertAndGetValue(2.2, 'notsupported', 'notsupported'); // 2.2
convertAndGetValue(2.2, 'kg/m3', 'sg'); // 2200
convertAndGetValue('2.2', 'kg/m3', 'sg'); // 2200
convertSamePrecision(numWithUnit, toUnit, digits?) {...}
Convert value with unit to another unit and display it in pretty format. It will preserv the number of digits in the input or alternativly converting to the given number of digits.
convertSamePrecision('1|in', 'cm', 8); // '2.54|cm'
convertSamePrecision('102e-6|in', 'cm'); // '0.000259|cm'
convertSamePrecision(2.54, 'cm', 1); // '3|cm'
convertSamePrecision('10.000|m', 'in'); // '393.7|in'
asFraction(str) {...}
Converts decimal number to fractional format return string with fractional format of set value
asFraction(''); // '0'
asFraction('0.1'); // '1/10'
numFraction(str) {...}
Convert fraction string to number (return input value if conversion fails) For historical reasons, numFraction returns the string value unmodified if it is not able to convert to a number. This is useful where user inputs are filtered through calls to numFraction. For "detecting" when numFraction fails, check if the return value is a string or a number. If it is a string it means number conversion failed. will return string with decimal format of fraction
numFraction(''); // ''
numFraction('1/10'); // 0.1
numFraction(null); // null
cleanNumStr(str){...}
Cleaning up and fixing provided number to correct numerical format removing redundant '.' dots, ',' commas, spaces
cleanNumStr('1000,000.1'); // '1000000.1'
cleanNumStr('1000,000,000'); // '1000000000'
cleanNumStr('1/10'); // '1/10'
cleanNumStr('1000,000,000.1.1'); // '100000000011'
cleanNum(str): {...}
Cleaning and fixing numerical string but returns it as number
cleanNum(1); // 1
cleanNum(',1'); // 0.1
toNum(input, defaultValue?, minimum?) {...}
Convert provided argument to number or return it if impossible to convert
toNum(1); // 1
toNum(',1'); // 0.1
isNonNumerical(value)
Check if value is non numerical
isNonNumerical('123.32asdasds4') // true
isNonNumerical('123.32') // false
Constants
LABELS
Units labels
LABELS.cm // 'cm'
LABELS.lps // 'L/s'
ALT_UNITS
Alternative units grouped by quantity
ALT_UNITS.angles // ['deg', 'rad']
ALT_UNITS.density // ['sg', 'ppg', 'kg/m3', 'lbm/ft3', 'g/cm3', 'lb/ft3']
UNIT_FROM_KEY
Units list
UNIT_FROM_KEY.length // 'm'
UNIT_FROM_KEY.latitude // '°N'
KNOWN_CONVERSIONS
Conversions list where each key 'from unit|to unit' pair
KNOWN_CONVERSIONS['m|mm'](1) // 1000
DEPRECATED_UNITS
List of deprecated units
DEPRECATED_UNITS['N-m'] // 'Nm'
DEPRECATED_UNITS['ft-lbf'] // 'ftlbf'
UNIT_ALIASES
This list is mapping from legal alternative unit names to our selected unit name
UNIT_ALIASES['lbs/ft'] // 'lb/ft'
INTERMEDIATE_CONVERSIONS
Intermediate conversions
INTERMEDIATE_CONVERSIONS.mm // 'm'
INTERMEDIATE_CONVERSIONS.t // 'kg'
SPECIAL_NUMBERS_STRING
Special numbers in string format
SPECIAL_NUMBERS_STRING // ['NaN', '-Infinity', 'Infinity']