import type { Fyo } from 'fyo'; import { Money } from 'pesa'; /** * And so should not contain and platforma specific imports. */ export function getValueMapFromList( list: T[], key: K, valueKey: V, filterUndefined = true ): Record { if (filterUndefined) { list = list.filter( (f) => (f[valueKey] as unknown) !== undefined && (f[key] as unknown) !== undefined ); } return list.reduce((acc, f) => { const keyValue = String(f[key]); const value = f[valueKey]; acc[keyValue] = value; return acc; }, {} as Record); } export function getRandomString(): string { const randomNumber = Math.random().toString(36).slice(2, 8); const currentTime = Date.now().toString(36); return `${randomNumber}-${currentTime}`; } export async function sleep(durationMilliseconds = 1000) { return new Promise((r) => setTimeout(() => r(null), durationMilliseconds)); } export function getMapFromList( list: T[], name: K ): Record { /** * Do not convert function to use copies of T * instead of references. */ const acc: Record = {}; for (const t of list) { const key = t[name]; if (key === undefined) { continue; } acc[String(key)] = t; } return acc; } export function getDefaultMapFromList( list: T[] | string[], defaultValue: D, name?: K ): Record { const acc: Record = {}; if (typeof list[0] === 'string') { for (const l of list as string[]) { acc[l] = defaultValue; } return acc; } if (!name) { return {}; } for (const l of list as T[]) { const key = String(l[name]); acc[key] = defaultValue; } return acc; } export function getListFromMap(map: Record): T[] { return Object.keys(map).map((n) => map[n]); } export function getIsNullOrUndef(value: unknown): value is null | undefined { return value === null || value === undefined; } export function titleCase(phrase: string): string { return phrase .split(' ') .map((word) => { const wordLower = word.toLowerCase(); if (['and', 'an', 'a', 'from', 'by', 'on'].includes(wordLower)) { return wordLower; } return wordLower[0].toUpperCase() + wordLower.slice(1); }) .join(' '); } export function invertMap(map: Record): Record { const keys = Object.keys(map); const inverted: Record = {}; for (const key of keys) { const val = map[key]; inverted[val] = key; } return inverted; } export function time(func: (...args: K[]) => T, ...args: K[]): T { /* eslint-disable no-console */ const name = func.name; console.time(name); const stuff = func(...args); console.timeEnd(name); return stuff; } export async function timeAsync( func: (...args: K[]) => Promise, ...args: K[] ): Promise { /* eslint-disable no-console */ const name = func.name; console.time(name); const stuff = await func(...args); console.timeEnd(name); return stuff; } export function changeKeys( source: Record, keyMap: Record ) { const dest: Record = {}; for (const key of Object.keys(source)) { const newKey = keyMap[key] ?? key; dest[newKey] = source[key]; } return dest; } export function deleteKeys( source: Record, keysToDelete: string[] ) { const dest: Record = {}; for (const key of Object.keys(source)) { if (keysToDelete.includes(key)) { continue; } dest[key] = source[key]; } return dest; } function safeParseNumber(value: unknown, parser: (v: string) => number) { let parsed: number; switch (typeof value) { case 'string': parsed = parser(value); break; case 'number': parsed = value; break; default: parsed = Number(value); break; } if (Number.isNaN(parsed)) { return 0; } return parsed; } export function safeParseFloat(value: unknown): number { return safeParseNumber(value, Number); } export function safeParseInt(value: unknown): number { return safeParseNumber(value, (v: string) => Math.trunc(Number(v))); } export function safeParsePesa(value: unknown, fyo: Fyo): Money { if (value instanceof Money) { return value; } if (typeof value === 'number') { return fyo.pesa(value); } if (typeof value === 'bigint') { return fyo.pesa(value); } if (typeof value !== 'string') { return fyo.pesa(0); } try { return fyo.pesa(value); } catch { return fyo.pesa(0); } } export function joinMapLists( listA: A[], listB: B[], keyA: keyof A, keyB: keyof B ): (A & B)[] { const mapA = getMapFromList(listA, keyA); const mapB = getMapFromList(listB, keyB); const keyListA = listA .map((i) => i[keyA]) .filter((k) => (k as unknown as string) in mapB); const keyListB = listB .map((i) => i[keyB]) .filter((k) => (k as unknown as string) in mapA); const keys = new Set([keyListA, keyListB].flat().sort()); const joint: (A & B)[] = []; for (const k of keys) { const a = mapA[k as unknown as string]; const b = mapB[k as unknown as string]; const c = { ...a, ...b }; joint.push(c); } return joint; } export function removeAtIndex(array: T[], index: number): T[] { if (index < 0 || index >= array.length) { return array; } return [...array.slice(0, index), ...array.slice(index + 1)]; } /** * Asserts that `value` is of type T. Use with care. */ export const assertIsType = (value: unknown): value is T => true;