Skip to content

Commit 35f46c9

Browse files
volivajosepot
authored andcommitted
Rename mergeActiveKeys for combineKeys, add tests and jsdocs
1 parent f976661 commit 35f46c9

File tree

3 files changed

+137
-5
lines changed

3 files changed

+137
-5
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Observable } from "rxjs"
2+
import { map, scan } from "rxjs/operators"
3+
import { TestScheduler } from "rxjs/testing"
4+
import { combineKeys } from "./"
5+
6+
const scheduler = () =>
7+
new TestScheduler((actual, expected) => {
8+
expect(actual).toEqual(expected)
9+
})
10+
11+
describe("combineKeys", () => {
12+
it("emits a map with the latest value of the stream of each key", () => {
13+
scheduler().run(({ expectObservable, cold }) => {
14+
const keys = cold(" ab---cd---").pipe(scan((acc, v) => [...acc, v], []))
15+
const a = cold(" --1---2---")
16+
const b = cold(" ---------")
17+
const c = cold(" 1----")
18+
const d = cold(" 9---")
19+
const expectedStr = "--e--f(gh)"
20+
21+
const innerStreams = { a, b, c, d }
22+
23+
const result = combineKeys(
24+
keys,
25+
(v): Observable<string> => innerStreams[v],
26+
).pipe(map((x) => Object.fromEntries(x.entries())))
27+
28+
expectObservable(result).toBe(expectedStr, {
29+
e: { a: "1" },
30+
f: { a: "1", c: "1" },
31+
g: { a: "2", c: "1" },
32+
h: { a: "2", c: "1", d: "9" },
33+
})
34+
})
35+
})
36+
37+
it("removes the entry of a key when that key is removed from the source", () => {
38+
scheduler().run(({ expectObservable, cold }) => {
39+
const keys = cold(" a-b---c---").pipe(map((v) => [v]))
40+
const a = cold(" ----------")
41+
const b = cold(" -1-2-3-4")
42+
const c = cold(" -2-3")
43+
const expectedStr = "---e-fgh-i"
44+
45+
const innerStreams = { a, b, c }
46+
47+
const result = combineKeys(
48+
keys,
49+
(v): Observable<string> => innerStreams[v],
50+
).pipe(map((x) => Object.fromEntries(x.entries())))
51+
52+
expectObservable(result).toBe(expectedStr, {
53+
e: { b: "1" },
54+
f: { b: "2" },
55+
g: {},
56+
h: { c: "2" },
57+
i: { c: "3" },
58+
})
59+
})
60+
})
61+
62+
it("completes when the key stream completes", () => {
63+
scheduler().run(({ expectObservable, cold }) => {
64+
const keys = cold(" a-b---|").pipe(scan((acc, v) => [...acc, v], []))
65+
const a = cold(" -1-----")
66+
const b = cold(" -1---")
67+
const expectedStr = "-e-f--|"
68+
69+
const innerStreams = { a, b }
70+
71+
const result = combineKeys(
72+
keys,
73+
(v): Observable<string> => innerStreams[v],
74+
).pipe(map((x) => Object.fromEntries(x.entries())))
75+
76+
expectObservable(result).toBe(expectedStr, {
77+
e: { a: "1" },
78+
f: { a: "1", b: "1" },
79+
})
80+
})
81+
})
82+
83+
it("propagates errors from the inner streams", () => {
84+
scheduler().run(({ expectObservable, cold }) => {
85+
const keys = cold(" a-b---|").pipe(scan((acc, v) => [...acc, v], []))
86+
const a = cold(" -1-----")
87+
const b = cold(" -#")
88+
const expectedStr = "-e-#"
89+
90+
const innerStreams = { a, b }
91+
92+
const result = combineKeys(
93+
keys,
94+
(v): Observable<string> => innerStreams[v],
95+
).pipe(map((x) => Object.fromEntries(x.entries())))
96+
97+
expectObservable(result).toBe(expectedStr, {
98+
e: { a: "1" },
99+
})
100+
})
101+
})
102+
103+
it("propagates errors from the key stream", () => {
104+
scheduler().run(({ expectObservable, cold }) => {
105+
const keys = cold(" a-b#").pipe(scan((acc, v) => [...acc, v], []))
106+
const a = cold(" -1--")
107+
const b = cold(" -1")
108+
const expectedStr = "-e-#"
109+
110+
const innerStreams = { a, b }
111+
112+
const result = combineKeys(
113+
keys,
114+
(v): Observable<string> => innerStreams[v],
115+
).pipe(map((x) => Object.fromEntries(x.entries())))
116+
117+
expectObservable(result).toBe(expectedStr, {
118+
e: { a: "1" },
119+
})
120+
})
121+
})
122+
})

packages/utils/src/mergeByKey.ts renamed to packages/utils/src/combineKeys.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { Observable, Subscription } from "rxjs"
22

3-
export const mergeActiveKeys = <K, T>(
4-
activeKeys$: Observable<Iterable<K>>,
3+
/**
4+
* Creates a stream that combines the result of the streams from each key of the input stream.
5+
*
6+
* @param keys$ Stream of the list of keys to subscribe to.
7+
* @param getInner$ Function that returns the stream for each key.
8+
* @returns An stream with a map containing the latest value from the stream of each key.
9+
*/
10+
export const combineKeys = <K, T>(
11+
keys$: Observable<Array<K> | Set<K>>,
512
getInner$: (key: K) => Observable<T>,
613
): Observable<Map<K, T>> =>
714
new Observable((observer) => {
@@ -12,16 +19,18 @@ export const mergeActiveKeys = <K, T>(
1219
if (!updatingSource) observer.next(new Map(currentValue))
1320
}
1421

15-
const subscription = activeKeys$.subscribe(
22+
const subscription = keys$.subscribe(
1623
(nextKeysArr) => {
1724
updatingSource = true
1825
const nextKeys = new Set(nextKeysArr)
1926
let changes = false
2027
innerSubscriptions.forEach((sub, key) => {
2128
if (!nextKeys.has(key)) {
22-
changes = true
2329
sub.unsubscribe()
24-
currentValue.delete(key)
30+
if (currentValue.has(key)) {
31+
changes = true
32+
currentValue.delete(key)
33+
}
2534
} else {
2635
nextKeys.delete(key)
2736
}

packages/utils/src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { collectValues } from "./collectValues"
22
export { collect } from "./collect"
3+
export { combineKeys } from "./combineKeys"
34
export { getGroupedObservable } from "./getGroupedObservable"
45
export { createSignal } from "./createSignal"
56
export { createKeyedSignal } from "./createKeyedSignal"

0 commit comments

Comments
 (0)