import { isPlainObject, concat, isMap, isSet, reduce } from '@dazl/common'; import { isSetMultiMap, SetMultiMap } from '@dazl/patterns'; export const mergeAll = (results: Iterable): T => reduce( results, // @ts-expect-error making overloading the function redundant (merged, newItem) => mergeResults(merged, newItem) as T, undefined as unknown as T, ); export function mergeResults(a?: Map, b?: Map): Map; export function mergeResults(a?: Set, b?: Set): Set; export function mergeResults(a?: SetMultiMap, b?: SetMultiMap): SetMultiMap; export function mergeResults(a?: M1, b?: M2): M1 & M2; export function mergeResults | Set | SetMultiMap | object, K, V>(a?: M, b?: M): M { if (a === undefined || b === undefined) { return (a ?? b) as M; } if (isPlainObject(a) && isPlainObject(b)) { return reduce( concat(Object.entries(a), Object.entries(b)), (result, [key, value]) => { result[key] = mergeResults(result[key], value as object); return result; }, {} as any, ) as M; } const type = getIterableType(a, b); if (type) { return new type(concat(a as Iterable, b as Iterable)) as M; } return b; // override older results } const getIterableType = (a: any, b: any) => checkType(a, b, isMap, Map) || checkType(a, b, isSet, Set) || checkType(a, b, isSetMultiMap, SetMultiMap); function checkType(a: any, b: any, check: (a: any) => boolean, type: any) { if (check(a)) { if (check(b)) { return type; } else { throw new Error(`Invalid results: type mismatch, expected ${type.name}, got ${b?.constructor?.name}`); } } return undefined; }