@oliasoft-open-source/units
TypeScript icon, indicating that this package has built-in type declarations

3.11.9 • Public • Published

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']

Readme

Keywords

none

Package Sidebar

Install

npm i @oliasoft-open-source/units

Weekly Downloads

1,525

Version

3.11.9

License

MIT

Unpacked Size

479 kB

Total Files

78

Last publish

Collaborators

  • mtmacdonald
  • kjeldahl
  • olegkabachii