@@ -9,12 +9,14 @@ interface BasicGroupByOptions<K, T> {
9
9
element ?: undefined ;
10
10
duration ?: ( grouped : GroupedObservable < K , T > ) => ObservableInput < any > ;
11
11
connector ?: ( ) => SubjectLike < T > ;
12
+ seed ?: ( ) => ObservableInput < K > ;
12
13
}
13
14
14
15
interface GroupByOptionsWithElement < K , E , T > {
15
16
element : ( value : T ) => E ;
16
17
duration ?: ( grouped : GroupedObservable < K , E > ) => ObservableInput < any > ;
17
18
connector ?: ( ) => SubjectLike < E > ;
19
+ seed ?: ( ) => ObservableInput < K > ;
18
20
}
19
21
20
22
export function groupBy < T , K > ( key : ( value : T ) => K , options : BasicGroupByOptions < K , T > ) : OperatorFunction < T , GroupedObservable < K , T > > ;
@@ -155,10 +157,11 @@ export function groupBy<T, K, R>(
155
157
) : OperatorFunction < T , GroupedObservable < K , R > > {
156
158
return operate ( ( source , subscriber ) => {
157
159
let element : ( ( value : any ) => any ) | void ;
160
+ let seed : ( ( ) => ObservableInput < K > ) | undefined ;
158
161
if ( ! elementOrOptions || typeof elementOrOptions === 'function' ) {
159
162
element = elementOrOptions ;
160
163
} else {
161
- ( { duration, element, connector } = elementOrOptions ) ;
164
+ ( { duration, element, connector, seed } = elementOrOptions ) ;
162
165
}
163
166
164
167
// A lookup for the groups that we have so far.
@@ -189,45 +192,7 @@ export function groupBy<T, K, R>(
189
192
// OperatorSubscriber will only send the error to the main subscriber.
190
193
try {
191
194
const key = keySelector ( value ) ;
192
-
193
- let group = groups . get ( key ) ;
194
- if ( ! group ) {
195
- // Create our group subject
196
- groups . set ( key , ( group = connector ? connector ( ) : new Subject < any > ( ) ) ) ;
197
-
198
- // Emit the grouped observable. Note that we can't do a simple `asObservable()` here,
199
- // because the grouped observable has special semantics around reference counting
200
- // to ensure we don't sever our connection to the source prematurely.
201
- const grouped = createGroupedObservable ( key , group ) ;
202
- subscriber . next ( grouped ) ;
203
-
204
- if ( duration ) {
205
- const durationSubscriber = new OperatorSubscriber (
206
- // Providing the group here ensures that it is disposed of -- via `unsubscribe` --
207
- // wnen the duration subscription is torn down. That is important, because then
208
- // if someone holds a handle to the grouped observable and tries to subscribe to it
209
- // after the connection to the source has been severed, they will get an
210
- // `ObjectUnsubscribedError` and know they can't possibly get any notifications.
211
- group as any ,
212
- ( ) => {
213
- // Our duration notified! We can complete the group.
214
- // The group will be removed from the map in the teardown phase.
215
- group ! . complete ( ) ;
216
- durationSubscriber ?. unsubscribe ( ) ;
217
- } ,
218
- // Completions are also sent to the group, but just the group.
219
- undefined ,
220
- // Errors on the duration subscriber are sent to the group
221
- // but only the group. They are not sent to the main subscription.
222
- undefined ,
223
- // Teardown: Remove this group from our map.
224
- ( ) => groups . delete ( key )
225
- ) ;
226
-
227
- // Start our duration notifier.
228
- groupBySourceSubscriber . add ( innerFrom ( duration ( grouped ) ) . subscribe ( durationSubscriber ) ) ;
229
- }
230
- }
195
+ const group = groups . get ( key ) ?? createGroup ( key ) ;
231
196
232
197
// Send the value to our group.
233
198
group . next ( element ? element ( value ) : value ) ;
@@ -246,9 +211,60 @@ export function groupBy<T, K, R>(
246
211
( ) => groups . clear ( )
247
212
) ;
248
213
214
+ if ( seed !== undefined ) {
215
+ groupBySourceSubscriber . add (
216
+ innerFrom ( seed ( ) ) . subscribe ( {
217
+ next : ( value ) => void ( groups . has ( value ) || createGroup ( value ) ) ,
218
+ complete : ( ) => subscriber . complete ( ) ,
219
+ error : handleError ,
220
+ } )
221
+ ) ;
222
+ }
223
+
249
224
// Subscribe to the source
250
225
source . subscribe ( groupBySourceSubscriber ) ;
251
226
227
+ function createGroup ( key : K ) : SubjectLike < T > {
228
+ // Create our group subject
229
+ const group = connector ? connector ( ) : new Subject < any > ( ) ;
230
+ groups . set ( key , group ) ;
231
+
232
+ // Emit the grouped observable. Note that we can't do a simple `asObservable()` here,
233
+ // because the grouped observable has special semantics around reference counting
234
+ // to ensure we don't sever our connection to the source prematurely.
235
+ const grouped = createGroupedObservable ( key , group ) ;
236
+ subscriber . next ( grouped ) ;
237
+
238
+ if ( duration ) {
239
+ const durationSubscriber = new OperatorSubscriber (
240
+ // Providing the group here ensures that it is disposed of -- via `unsubscribe` --
241
+ // when the duration subscription is torn down. That is important, because then
242
+ // if someone holds a handle to the grouped observable and tries to subscribe to it
243
+ // after the connection to the source has been severed, they will get an
244
+ // `ObjectUnsubscribedError` and know they can't possibly get any notifications.
245
+ group as any ,
246
+ ( ) => {
247
+ // Our duration notified! We can complete the group.
248
+ // The group will be removed from the map in the teardown phase.
249
+ group . complete ( ) ;
250
+ durationSubscriber . unsubscribe ( ) ;
251
+ } ,
252
+ // Completions are also sent to the group, but just the group.
253
+ undefined ,
254
+ // Errors on the duration subscriber are sent to the group
255
+ // but only the group. They are not sent to the main subscription.
256
+ undefined ,
257
+ // Teardown: Remove this group from our map.
258
+ ( ) => groups . delete ( key )
259
+ ) ;
260
+
261
+ // Start our duration notifier.
262
+ groupBySourceSubscriber . add ( innerFrom ( duration ( grouped ) ) . subscribe ( durationSubscriber ) ) ;
263
+ }
264
+
265
+ return group ;
266
+ }
267
+
252
268
/**
253
269
* Creates the actual grouped observable returned.
254
270
* @param key The key of the group
0 commit comments