diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 430f360..4fa4ee7 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,2 +1,3 @@ +export * from './iterators'; export * from './time'; export * from './values'; diff --git a/src/helpers/iterators.ts b/src/helpers/iterators.ts new file mode 100644 index 0000000..0bbc61d --- /dev/null +++ b/src/helpers/iterators.ts @@ -0,0 +1,78 @@ +import { logger } from '../logger'; +import { isDevEnv } from './values'; + +/** + * Iterate over an array, yielding multiple items at a time. If the size of the given array + * is not divisible by the given batch size, then the length of the last items returned will + * be smaller than the given batch size, i.e.: + * ```typescript + * items.length % batchSize + * ``` + * @param items - The array to iterate over. + * @param batchSize - Maximum number of items to return at a time. + * @param printBenchmark - If we should print benchmark of items per second + */ +export function* batchIterate( + items: T[], + batchSize: number, + printBenchmark = isDevEnv +): Generator { + if (items.length === 0) return; + const startTime = Date.now(); + for (let i = 0; i < items.length; ) { + const itemsRemaining = items.length - i; + const sliceSize = Math.min(batchSize, itemsRemaining); + yield items.slice(i, i + sliceSize); + i += sliceSize; + } + if (printBenchmark) { + const itemsPerSecond = Math.round((items.length / (Date.now() - startTime)) * 1000); + const caller = new Error().stack?.split('at ')[3].trim(); + logger.debug(`Iterated ${itemsPerSecond} items/second at ${caller}`); + } +} + +/** + * Iterate over an `AsyncIterable`, yielding multiple items at a time. If the size of the given + * array is not divisible by the given batch size, then the length of the last items returned will + * be smaller than the given batch size. + * + * @param items - AsyncIterable + * @param batchSize - Batch size + * @param printBenchmark - If we should print benchmark of items per second + */ +export async function* asyncBatchIterate( + items: AsyncIterable, + batchSize: number, + printBenchmark = isDevEnv +): AsyncGenerator { + const startTime = Date.now(); + let itemCount = 0; + let itemBatch: T[] = []; + for await (const item of items) { + itemBatch.push(item); + itemCount++; + if (itemBatch.length >= batchSize) { + yield itemBatch; + itemBatch = []; + if (printBenchmark) { + const itemsPerSecond = Math.round((itemCount / (Date.now() - startTime)) * 1000); + const caller = new Error().stack?.split('at ')[3].trim(); + logger.debug(`Iterated ${itemsPerSecond} items/second at ${caller}`); + } + } + } + if (itemBatch.length > 0) { + yield itemBatch; + } +} + +/** + * Convert an `AsyncIterable` to a generator + * @param iter - AsyncIterable + */ +export async function* asyncIterableToGenerator(iter: AsyncIterable) { + for await (const entry of iter) { + yield entry; + } +} diff --git a/src/helpers/values.ts b/src/helpers/values.ts index 6f7dd98..cc1a34e 100644 --- a/src/helpers/values.ts +++ b/src/helpers/values.ts @@ -130,3 +130,15 @@ export function numberToHex(number: number, paddingBytes: number = 4): string { * @returns Boolean */ export const has0xPrefix = (val: string) => val.substring(0, 2).toLowerCase() === '0x'; + +/** + * Converts a string to an enum value. + * @param enumType - The enum type + * @param value - The string value to convert + * @returns Enum item or undefined + */ +export function toEnumValue(enm: { [s: string]: T }, value: string): T | undefined { + return (Object.values(enm) as unknown as string[]).includes(value) + ? (value as unknown as T) + : undefined; +}