export type GetNext = (keys: T[]) => T; export type GetKeys = (state: T) => K[]; export const getObjectKeys: GetKeys, string> = ( state: Record, ): string[] => Object.keys(state); export const getObjectKeysAsNumbers: GetKeys, number> = ( state: Record, ): number[] => Object.keys(state).map((key) => Number.parseInt(key, 10)); export const getNextNumberLikeStringKey: GetNext<`${number}`> = (keys: `${number}`[]) => ( keys.map((key) => Number.parseInt(key, 10)).reduce((a, b) => (a > b ? a : b), 0) + 1 ).toString() as `${number}`; export const getNextLargestNumber: GetNext = (keys: number[]): number => keys.reduce((a, b) => (a > b ? a : b), 0) + 1; export const getNextSmallestNumber: GetNext = (keys: number[]): number => { const sortedKeys = [...keys].sort((a, b) => a - b); for (let i = 0; i < sortedKeys.length; i++) { if (!sortedKeys.includes(i + 1)) { return i + 1; } } return (sortedKeys.at(-1) ?? 0) + 1; }; export enum PremadeGetNext { nextLargest = 'nextlargest', nextSmallest = 'nextsmallest', } export type NextKeyStrategy = PremadeGetNext | GetNext; export const getNextKeyStrategy = (nextKeyStrategy?: NextKeyStrategy): GetNext => { if (typeof nextKeyStrategy === 'function') { return nextKeyStrategy; } else if (nextKeyStrategy === PremadeGetNext.nextLargest) { return getNextLargestNumber; } else if (nextKeyStrategy === PremadeGetNext.nextSmallest) { return getNextSmallestNumber; } else { return getNextLargestNumber; } };