UNPKG

@bitblit/ratchet-common

Version:

Common tools for general use

201 lines 7.33 kB
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