export type Fn = (a: A) => B; export const flatten = (arr: T[][]) => arr.reduce((acc, item) => acc.concat(item), []); export const fst = (arr: T[]) => arr[0]; export const encodePayload = (data: any) => Buffer.from(JSON.stringify(data)).toString('base64'); type IPredicate = (value: T) => boolean; export const andFilter = (predicates: IPredicate[]): IPredicate => (value: T) => { return andPredicates(predicates, value); }; export const andPredicates = (predicates: IPredicate[], value: T): boolean => { if (predicates.length === 0) { return true; } const [firstPredicate, ...others] = predicates; return firstPredicate(value) && andPredicates(others, value); }; export function addTime(baseMs: number, offsetMs: number): number { const date = new Date(baseMs); date.setMilliseconds(date.getMilliseconds() + offsetMs); return date.getTime(); } export function getNextTimeThreshold(baseTimestamp: number, offsetMs: number): number { const nextDate = baseTimestamp + offsetMs; return offsetMs < DAY ? offsetMs < HOUR ? new Date(nextDate).setSeconds(0, 0) : new Date(nextDate).setMinutes(0, 0, 0) : new Date(nextDate).setHours(0, 0, 0, 0); } export function isLastIndex(index: number, arr: T[]): boolean { return index === arr.length - 1; } export const not = (fn: Fn) => (value: I) => !fn(value); export const sanitizeLocale = (locale: string) => locale.replace(/zh_.*/, 'zh'); export const SECOND = 1000; export const MINUTE = 60 * SECOND; export const HOUR = 60 * MINUTE; export const DAY = 24 * HOUR; const SPECIAL_CHARACTERS = [ '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~', ]; const specialCharactersTemplate = new RegExp(`[${SPECIAL_CHARACTERS.join('')}]`, 'gi'); export const sanitizeString = (str: string) => str.replace(/\s+/g, ' ').replace(specialCharactersTemplate, ''); export const stringToList = (separator: string) => (value: string): string[] => { if (!value) { return []; } return value .trim() .split(separator) .map((v) => v.trim()); }; export const arr2Obj = (arr: [string, V][]): Record => { return arr.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); }; export const omit = (obj: Record, key: string) => { return Object.keys(obj).reduce>((acc, field) => { if (key !== field) { acc[field] = obj[field]; } return acc; }, {}); }; const placeholderPattern = new RegExp(/\{.+?\}/gm); export const template = ( str: string, values: Record = {}, fallbackValues: Record = {}, ): string => { if (!str) { throw new Error('No str provided'); } const placeholders = str.match(placeholderPattern); return placeholders ? placeholders .map((v) => v.slice(1, -1)) .reduce((newStr, placeholder) => { const placeholderValue = values[placeholder] || fallbackValues[placeholder]; return placeholderValue === undefined || placeholderValue === null ? newStr : newStr.replace(`{${placeholder}}`, placeholderValue.toString()); }, str) : str; }; export function secToHrs(qty: number) { return qty / 60 / 60; } export function globPattern(keys: string[]) { return encodeURIComponent('{' + keys.join(',') + '}'); } export const distinct = (arr: T[]) => Array.from(new Set(arr)); export const sleep = (time: number) => new Promise((resolve) => setTimeout(resolve, time));