@bitblit/ratchet-common
Version:
Common tools for general use
201 lines • 7.33 kB
JavaScript
import { Logger } from '../logger/logger.js';
import { RequireRatchet } from './require-ratchet.js';
export class NumberRatchet {
static MAX_LEADING_ZEROS_FORMAT_LENGTH = 1000;
static DEFAULT_SAFE_NUMBER_OPTIONS = {
ifNotNumber: null,
returnValueForNull: null,
returnValueForUndefined: null,
preParseCharacterMapping: { ',': '' },
};
static toFixedDecimalNumber(input, placesAfterPoint) {
const v = NumberRatchet.safeNumber(input);
return v === null || v === undefined ? v : NumberRatchet.safeNumber(v.toFixed(placesAfterPoint));
}
static leadingZeros(val, size) {
const sVal = String(val);
if (sVal.length < size) {
let pad = '0000';
if (size > NumberRatchet.MAX_LEADING_ZEROS_FORMAT_LENGTH) {
throw 'Cannot format number that large (max length is ' + NumberRatchet.MAX_LEADING_ZEROS_FORMAT_LENGTH + ')';
}
while (pad.length < size) {
pad = pad + pad;
}
return (pad + sVal).slice(-1 * size);
}
else {
return sVal;
}
}
static between(inTest, inP1, inP2) {
const test = NumberRatchet.safeNumber(inTest);
const p1 = NumberRatchet.safeNumber(inP1);
const p2 = NumberRatchet.safeNumber(inP2);
return (test >= p1 && test <= p2) || (test >= p2 && test <= p1);
}
static safeNumber(input, ifNotNumber = null, useDefaultForNullAndUndefined) {
const opts = {
ifNotNumber: ifNotNumber,
returnValueForNull: useDefaultForNullAndUndefined ? ifNotNumber : null,
returnValueForUndefined: useDefaultForNullAndUndefined ? ifNotNumber : undefined,
};
if (useDefaultForNullAndUndefined === undefined) {
opts.returnValueForUndefined = null;
}
return NumberRatchet.safeNumberOpt(input, opts);
}
static safeNumberOpt(input, optionPart) {
let rval = undefined;
const opts = Object.assign({}, NumberRatchet.DEFAULT_SAFE_NUMBER_OPTIONS, optionPart || {});
if (input === null) {
rval = opts.returnValueForNull;
}
else if (input === undefined) {
rval = opts.returnValueForUndefined;
}
else {
const type = typeof input;
if (type == 'number') {
rval = input;
}
else if (type == 'string') {
let test = input.trim();
if (test.length === 0) {
rval = opts.ifNotNumber;
}
else {
if (opts.preParseCharacterMapping && Object.keys(opts.preParseCharacterMapping).length > 0) {
let t2 = '';
for (let i = 0; i < test.length; i++) {
const cr = test.charAt(i);
t2 += opts.preParseCharacterMapping[cr] === undefined ? cr : opts.preParseCharacterMapping[cr];
}
test = t2;
}
rval = Number.parseFloat(test);
}
}
else {
Logger.warn('Value is of type %s, returning default', type);
rval = opts.ifNotNumber;
}
if (isNaN(rval)) {
Logger.debug('Parsed string to NaN - using NaN value from param');
rval = opts.ifNotNumber;
}
}
return rval;
}
static numberCSVToList(inputCSV) {
let rval = null;
if (inputCSV) {
rval = inputCSV.split(',').map((s) => {
return NumberRatchet.safeNumber(s.trim());
});
rval = rval.filter((r) => typeof r === 'number' && !isNaN(r));
}
return rval;
}
static fitToWindow(val, b1, b2) {
let rval = val;
if (val === null || b1 === null || b2 === null || b1 < 0 || b2 < 0 || val < 0) {
throw new Error('All values must be non-null and larger than 0');
}
const low = Math.min(b1, b2);
const high = Math.max(b1, b2);
const windowSize = high - low;
if (high === low) {
rval = high;
}
else {
while (rval < low) {
rval += windowSize;
}
while (rval > high) {
rval -= windowSize;
}
}
return rval;
}
static groupNumbersIntoContiguousRanges(inputData, minRangeSize = 3) {
RequireRatchet.notNullOrUndefined(inputData);
const input = Object.assign([], inputData);
input.sort((a, b) => a - b);
const singles = [];
const ranges = [];
let start = 0;
for (let i = 1; i < input.length; i++) {
if (input[i] === input[i - 1] + 1) {
}
else {
if (start === i - 1) {
singles.push(input[i - 1]);
}
else {
const rangeSize = i - start;
if (rangeSize < minRangeSize) {
for (let j = start; j < i; j++) {
singles.push(input[j]);
}
}
else {
ranges.push({
min: input[start],
max: input[i - 1],
});
}
}
start = i;
}
}
return {
singles: singles,
ranges: ranges,
};
}
static distributeItemsEvenly(items, buckets) {
RequireRatchet.notNullOrUndefined(items, 'items');
RequireRatchet.notNullOrUndefined(buckets, 'buckets');
RequireRatchet.true(items > 0 && items === Math.floor(items), 'Items integer larger than 0');
RequireRatchet.true(buckets > 0 && buckets === Math.floor(buckets), 'Buckets integer larger than 0');
const offset = buckets / items;
const rval = [];
for (let i = 0; i < buckets; i++) {
rval.push(0);
}
let loc = 0;
let rem = items;
while (rem > 0) {
rval[Math.floor(loc) % buckets]++;
rem--;
loc += offset;
}
return rval;
}
static createRange(minInclusive, maxExclusive, step = 1) {
const rval = [];
let val = minInclusive;
while (val < maxExclusive) {
rval.push(val);
val += step;
}
return rval;
}
static percentFormatNumberRange(range) {
let rval = 'N/A';
if (range) {
rval = range.low ? NumberRatchet.pctFormatted(range.low) : ' ^ ';
rval += ' - ';
rval += range.high ? NumberRatchet.pctFormatted(range.high) : ' ^ ';
}
return rval;
}
static pctFormatted(pct, fractionDigits = 2) {
const rval = pct === null || pct === undefined
? 'Null'
: pct.toLocaleString('en-US', { minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits }) + '%';
return rval;
}
}
//# sourceMappingURL=number-ratchet.js.map