UNPKG

dbl-utils

Version:

Utilities for dbl, adba and others projects

331 lines (330 loc) 11.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LCG = void 0; exports.sliceIntoChunks = sliceIntoChunks; exports.splitAndFlat = splitAndFlat; exports.generateRandomColors = generateRandomColors; exports.evaluateColorSimilarity = evaluateColorSimilarity; exports.normalize = normalize; exports.slugify = slugify; exports.randomS4 = randomS4; exports.randomString = randomString; exports.timeChunks = timeChunks; exports.delay = delay; exports.hash = hash; const chroma_js_1 = __importDefault(require("chroma-js")); const moment_1 = __importDefault(require("moment")); //----------------------------------Arrays /** * Splits an array into chunks of a given size. * @param arr - Array to split into chunks. * @param chunkSize - Size of each chunk. * @returns Array of chunked arrays. * * @example * ```ts * sliceIntoChunks([1,2,3,4], 2); // [[1,2],[3,4]] * ``` */ function sliceIntoChunks(arr, chunkSize) { const res = []; for (let i = 0; i < arr.length; i += chunkSize) { const chunk = arr.slice(i, i + chunkSize); res.push(chunk); } return res; } /** * Splits and flattens a list of strings based on a separator. * @param {string[]} arrayStrings - Array of strings to split and flatten. * @param {string} separator - Separator to use for splitting. * @returns {string[]} Flattened and splitted array of strings. * * @example * ```ts * splitAndFlat(['tag1 tag2', 'tag2'], ' '); * // => ['tag1', 'tag2'] * ``` */ function splitAndFlat(arrayStrings, separator = ' ') { const r = arrayStrings.flatMap(s => (typeof s === 'string' && s.split(separator))); const filtered = r.filter(Boolean); return Array.from(new Set(filtered)); } //-----------------------------------colors /** * Generates a random set of colors with an optional format. * @param count - Number of colors to generate. * @param options - Object containing optional parameters. * @param options.format - The format of the output colors ('hex', 'rgb', 'nrgb'). * @param options.bounds - The bounds for color transformation. * @param options.distribute - Whether the colors should be evenly distributed. * @returns An array of colors in the specified format. * * @example * ```ts * generateRandomColors(2, { format: 'rgb' }); * // => [[r,g,b], [r,g,b]] * ``` */ function generateRandomColors(count, { format = 'hex', bounds = 1, distribute = true } = {}) { const uniqueColors = new Set(); if (distribute) { const step = Math.floor(0xFFFFFF / count); for (let i = 0; i < count; i++) { const colorValue = i * step; const colorHex = '#' + colorValue.toString(16).padStart(6, '0'); const color = (0, chroma_js_1.default)(colorHex); uniqueColors.add(color); } } else { while (uniqueColors.size < count) { uniqueColors.add(chroma_js_1.default.random()); } } const formattedColors = Array.from(uniqueColors).map((color) => { const [r, g, b] = color.rgb(); const adjustedColor = (typeof bounds === 'number' || Array.isArray(bounds)) ? color.rgb().map((c) => transform(c, bounds)) : [ transform(r, bounds.r), transform(g, bounds.g), transform(b, bounds.b) ]; return (0, chroma_js_1.default)(...adjustedColor); }); return formattedColors.map(color => { if (format === 'hex') { return color.hex(); } else if (format === 'rgb') { return color.rgb(); } else if (format === 'nrgb') { return color.rgb().map((c) => c / 255); } }); } /** * Transforms a color value based on the specified limit. * @param value - The color component value. * @param limit - The limit for transformation. * @returns The transformed color component. */ function transform(value, limit) { const [min, max] = Array.isArray(limit) ? limit : typeof limit === 'number' ? [0, limit] : [0, 1]; return ((value / 255) * (max - min) + min) * 255; } /** * Evaluates the similarity of a set of colors. * @param cls - Array of colors in hex format. * @returns A similarity score between 0 and 1, where 0 indicates all colors are distinct and 1 indicates all are similar. * * @example * ```ts * evaluateColorSimilarity(['#ffffff', '#fffffe']); * // => value close to 1 * ``` */ function evaluateColorSimilarity(colors) { let totalDistance = 0; let pairsCount = 0; const cls = colors.sort(); for (let i = 0; i < cls.length; i++) { for (let j = i + 1; j < cls.length; j++) { totalDistance += chroma_js_1.default.distance(cls[i], cls[j]); pairsCount++; } } const averageDistance = totalDistance / pairsCount; const maxDistance = Math.sqrt(Math.pow(255, 2) * 3); // Maximum possible distance in RGB return 1 - (averageDistance / maxDistance); } //-------------------------------------strings /** * Normalizes a string by converting it to lowercase and removing diacritical marks. * @param {string} str - The string to normalize. * @returns {string} The normalized string. * * @example * ```ts * normalize('Árbol'); * // => 'arbol' * ``` */ function normalize(str = '') { return str.toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, ''); } /** * Converts a string to a slug. * @param {string} str - The string to slugify. * @returns {string} The slugified string. * * @example * ```ts * slugify('Hello World!'); * // => 'hello-world' * ``` */ function slugify(str = '') { return normalize(str) .replace(/\s/g, '-') .replace(/[^a-zA-Z\d\-]+/g, '') .replace(/-+/g, '-'); } /** * Generates a random four-character string consisting of hexadecimal digits. * @returns {string} The random four-character string. * * @example * ```ts * randomS4(); * // => '9f3b' * ``` */ function randomS4() { return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); } /** * Generates a random string of the specified length using the provided characters. * * @param {number} [length=16] - The length of the generated string. Default is 16. * @param {string} [characters='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'] - A string representing the characters to be used. Default includes uppercase, lowercase letters, and digits. * @returns {string} A random string composed of the specified characters. * * @example * ```ts * randomString(5); * // => e.g., 'aB3dE' * ``` */ function randomString(length = 16, characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') { let result = ''; const charactersLength = characters.length; for (let i = 0; i < length; i++) { const randomIndex = Math.floor(Math.random() * charactersLength); result += characters.charAt(randomIndex); } return result; } //------------------------------------------time /** * Generates an array of time intervals between two dates. * @param {Object} options - Options for generating time intervals. * @param {string|number} options.from - Start date in string format or as a UNIX timestamp. * @param {string|number} options.to - End date in string format or as a UNIX timestamp. * @param {number} options.step - The step size in hours for each time interval. * @param {string} [options.boundary] - Restrict chunks to 'day', 'month', or 'day,month' boundaries. * @returns An array of objects representing each time interval. * * @example * ```ts * timeChunks({ from: '2020-01-01', to: '2020-01-02', step: 6 }); * ``` */ function timeChunks(options) { const { from, to, step, boundary } = options; if (!(step > 0)) { throw new Error('Invalid step ' + step); } if (!(0, moment_1.default)(from).isValid()) { throw new Error('Invalid date ' + from); } if (!(0, moment_1.default)(to).isValid()) { throw new Error('Invalid date ' + to); } const hoursArray = []; const fromMoment = Number.isInteger(from) ? moment_1.default.unix(from) : (0, moment_1.default)(from); const toMoment = Number.isInteger(to) ? moment_1.default.unix(to) : (0, moment_1.default)(to); const diffHours = toMoment.diff(fromMoment, 'hours'); for (let i = 0; i < diffHours; i += step) { let hourMoment = (0, moment_1.default)(from).add(i, 'hours'); let nextHourMoment = (0, moment_1.default)(from).add(i + step, 'hours'); if (boundary) { const boundaries = boundary.split(','); if (boundaries.includes('day')) { const endOfDay = (0, moment_1.default)(hourMoment).endOf('day'); nextHourMoment = nextHourMoment.isAfter(endOfDay) ? endOfDay : nextHourMoment; } if (boundaries.includes('month')) { const endOfMonth = (0, moment_1.default)(hourMoment).endOf('month'); nextHourMoment = nextHourMoment.isAfter(endOfMonth) ? endOfMonth : nextHourMoment; } } hoursArray.push({ from: hourMoment.format(), to: nextHourMoment.isAfter(toMoment) ? (0, moment_1.default)(to).format() : nextHourMoment.format(), step }); } return hoursArray; } /** * Resolves the promise after a given timeout. * @param {number} timeout - Milliseconds to wait before resolving. * @returns {Promise<void>} The promise that resolves after the timeout. * * @example * ```ts * await delay(1000); * // waits for one second * ``` */ function delay(timeout) { return new Promise(resolve => setTimeout(resolve, timeout)); } //-------------------------------------------------------- numbers /** * Computes the hash value of a string. * @param {string} string - The string to hash. * @returns {number} The computed hash value. * * @example * ```ts * hash('abc'); * // => deterministic numeric hash * ``` */ function hash(string) { let hash = 0, i, chr; for (i = 0; i < string.length; i++) { chr = string.charCodeAt(i); hash = ((hash << 5) - hash) + chr; hash |= 0; // Convert to 32-bit integer } return hash; } /** * Linear Congruential Generator (LCG) class for pseudo-random number generation. * @class LCG * * @example * ```ts * const gen = new LCG(123); * gen.random(); * // => 0.596735... (deterministic) * ``` */ class LCG { constructor(seed) { this.a = 1664525; this.c = 1013904223; this.m = Math.pow(2, 32); this.seed = seed; } /** * Generates a pseudo-random number between 0 and 1. * @returns {number} The generated pseudo-random number. */ random() { this.seed = (this.a * this.seed + this.c) % this.m; return this.seed / this.m; } } exports.LCG = LCG;