{"version":3,"file":"index.cjs","sources":["../src/mergeMapArray.ts"],"sourcesContent":["import {filter, merge, mergeMap, Observable, scan, share, takeUntil} from 'rxjs'\nimport {map, withLatestFrom} from 'rxjs/operators'\n\ntype State<T> = {\n  current: T[]\n  added: T[]\n  removed: T[]\n}\n\nconst INITIAL_STATE: State<never> = {\n  current: [],\n  added: [],\n  removed: [],\n}\n\nconst EMPTY_ARRAY: never[] = []\n/**\n * Operator that takes an array as input and emits a new array with the value of the projected observable for each item in the input array.\n * It creates a new inner observable for each unique element in the input array, and it will keep the subscription to the inner observable for as long as the input array includes the element.\n * The operator will emit the projected values in the same order as the input array, and if the order of elements in the input array changes, it will move items in the output array to match the new order.\n * @param project - Function that takes an item from the input array and returns an observable that emits the projected value.\n * @param isEqual - Optional function to compare items in the input array. Defaults to strict equality.\n * @public\n */\nexport function mergeMapArray<T, R>(\n  project: (item: T) => Observable<R>,\n  isEqual: (a: T, b: T) => boolean = (a, b) => a === b,\n): (source: Observable<T[]>) => Observable<R[]> {\n  return (input: Observable<T[]>): Observable<R[]> => {\n    const sharedInput = input.pipe(share())\n    const state$ = sharedInput\n      .pipe(\n        scan((state: State<T>, next: T[]) => {\n          const added = next.filter(\n            (item) => !state.current.find((current) => isEqual(current, item)),\n          )\n\n          const removed = state.current.filter(\n            (item) => !next.find((current) => isEqual(current, item)),\n          )\n\n          return {\n            current: next,\n            added: uniqueBy(added, isEqual),\n            removed: uniqueBy(removed, isEqual),\n          }\n        }, INITIAL_STATE),\n      )\n      .pipe(\n        share(),\n        filter((state) => state.added.length > 0 || state.removed.length > 0),\n      )\n\n    // emits elements as they are added to the input array\n    const removed$ = state$.pipe(mergeMap((state) => state.removed))\n\n    // emits elements as they are removed from the input array\n    const added$ = state$.pipe(mergeMap((state) => state.added))\n\n    // special case for empty input array since it won't trigger any emission on the \"add element\" stream\n    const empty = sharedInput.pipe(\n      filter((arr) => arr.length === 0),\n      map(() => EMPTY_ARRAY),\n    )\n\n    const mapped = added$.pipe(\n      mergeMap((element) => {\n        const removed = removed$.pipe(filter((k) => isEqual(k, element))).pipe(share())\n        return merge(\n          removed.pipe(map(() => ({type: 'remove', element}) as const)),\n          project(element).pipe(\n            takeUntil(removed),\n            map((projected) => ({type: 'emit', element, projected}) as const),\n          ),\n        )\n      }),\n      withLatestFrom(sharedInput),\n      scan((acc: (undefined | {item: T; emitted: boolean; value: R})[], [event, inputArray]) => {\n        return inputArray.flatMap((item) => {\n          if (isEqual(item, event.element)) {\n            return event.type === 'remove' ? [] : {item, emitted: true, value: event.projected}\n          }\n          // find the entry from the previous emission and move it to the right place\n          return acc.find((v) => v && isEqual(v.item, item))\n        })\n      }, []),\n      filter((v) => v.every((item) => item?.emitted)),\n      map((v) => v.map((item) => item!.value)),\n    )\n\n    return merge(empty, mapped)\n  }\n}\n\nfunction uniqueBy<T>(array: T[], predicate: (a: T, b: T) => boolean): T[] {\n  const deduped: T[] = []\n  let hasDuplicates = false\n  for (const item of array) {\n    const exists = deduped.find((previous) => predicate(previous, item))\n    if (exists) {\n      hasDuplicates = true\n    } else {\n      deduped.push(item)\n    }\n  }\n  return hasDuplicates ? deduped : array\n}\n"],"names":["share","scan","filter","mergeMap","map","merge","takeUntil","withLatestFrom"],"mappings":";;;AASA,MAAM,gBAA8B;AAAA,EAClC,SAAS,CAAC;AAAA,EACV,OAAO,CAAC;AAAA,EACR,SAAS,CAAC;AACZ,GAEM,cAAuB,CAAA;AAStB,SAAS,cACd,SACA,UAAmC,CAAC,GAAG,MAAM,MAAM,GACL;AAC9C,SAAO,CAAC,UAA4C;AAClD,UAAM,cAAc,MAAM,KAAKA,YAAO,GAChC,SAAS,YACZ;AAAA,MACCC,UAAK,CAAC,OAAiB,SAAc;AACnC,cAAM,QAAQ,KAAK;AAAA,UACjB,CAAC,SAAS,CAAC,MAAM,QAAQ,KAAK,CAAC,YAAY,QAAQ,SAAS,IAAI,CAAC;AAAA,QAAA,GAG7D,UAAU,MAAM,QAAQ;AAAA,UAC5B,CAAC,SAAS,CAAC,KAAK,KAAK,CAAC,YAAY,QAAQ,SAAS,IAAI,CAAC;AAAA,QAAA;AAGnD,eAAA;AAAA,UACL,SAAS;AAAA,UACT,OAAO,SAAS,OAAO,OAAO;AAAA,UAC9B,SAAS,SAAS,SAAS,OAAO;AAAA,QAAA;AAAA,SAEnC,aAAa;AAAA,IAAA,EAEjB;AAAA,MACCD,WAAM;AAAA,MACNE,KAAAA,OAAO,CAAC,UAAU,MAAM,MAAM,SAAS,KAAK,MAAM,QAAQ,SAAS,CAAC;AAAA,IAAA,GAIlE,WAAW,OAAO,KAAKC,KAAAA,SAAS,CAAC,UAAU,MAAM,OAAO,CAAC,GAGzD,SAAS,OAAO,KAAKA,cAAS,CAAC,UAAU,MAAM,KAAK,CAAC,GAGrD,QAAQ,YAAY;AAAA,MACxBD,KAAAA,OAAO,CAAC,QAAQ,IAAI,WAAW,CAAC;AAAA,MAChCE,UAAA,IAAI,MAAM,WAAW;AAAA,IAAA,GAGjB,SAAS,OAAO;AAAA,MACpBD,KAAA,SAAS,CAAC,YAAY;AACpB,cAAM,UAAU,SAAS,KAAKD,KAAA,OAAO,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,EAAE,KAAKF,KAAAA,MAAO,CAAA;AACvE,eAAAK,KAAA;AAAA,UACL,QAAQ,KAAKD,UAAAA,IAAI,OAAO,EAAC,MAAM,UAAU,QAAO,EAAW,CAAC;AAAA,UAC5D,QAAQ,OAAO,EAAE;AAAA,YACfE,KAAAA,UAAU,OAAO;AAAA,YACjBF,UAAA,IAAI,CAAC,eAAe,EAAC,MAAM,QAAQ,SAAS,YAAoB;AAAA,UAClE;AAAA,QAAA;AAAA,MACF,CACD;AAAA,MACDG,UAAAA,eAAe,WAAW;AAAA,MAC1BN,UAAK,CAAC,KAA4D,CAAC,OAAO,UAAU,MAC3E,WAAW,QAAQ,CAAC,SACrB,QAAQ,MAAM,MAAM,OAAO,IACtB,MAAM,SAAS,WAAW,CAAC,IAAI,EAAC,MAAM,SAAS,IAAM,OAAO,MAAM,UAAS,IAG7E,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,MAAM,IAAI,CAAC,CAClD,GACA,EAAE;AAAA,MACLC,KAAAA,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,QAAM,OAAA,SAAA,KAAA,OAAO,CAAC;AAAA,MAC9CE,cAAI,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,KAAM,KAAK,CAAC;AAAA,IAAA;AAGlC,WAAAC,KAAA,MAAM,OAAO,MAAM;AAAA,EAAA;AAE9B;AAEA,SAAS,SAAY,OAAY,WAAyC;AACxE,QAAM,UAAe,CAAA;AACrB,MAAI,gBAAgB;AACpB,aAAW,QAAQ;AACF,YAAQ,KAAK,CAAC,aAAa,UAAU,UAAU,IAAI,CAAC,IAEjE,gBAAgB,KAEhB,QAAQ,KAAK,IAAI;AAGrB,SAAO,gBAAgB,UAAU;AACnC;;"}