Skip to content

Commit 0671f0c

Browse files
32bitkidrpominov
authored andcommitted
#143 Make Kefir.combine() also accept sources as objects (#225)
1 parent 813e337 commit 0671f0c

File tree

4 files changed

+487
-169
lines changed

4 files changed

+487
-169
lines changed

docs-src/descriptions/multiple-sources.jade

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,40 @@ pre(title='events in time').
6161
result: ------•--•-•X
6262
9 12 14
6363

64+
div
65+
66+
p.
67+
Also, #[b combine] supports passing objects as both #[b obss] #[i and] #[b passiveObss].
68+
The #[b combinator] function will then be called with a single argument, a new object with
69+
the latest value from each observable. If no #[b combinator] is provided, it emits
70+
the object containing latest values.
71+
72+
pre.javascript(title='example')
73+
:escapehtml
74+
var aStream = Kefir.sequentially(100, [1, 3]);
75+
var bStream = Kefir.sequentially(100, [2, 4]).delay(40);
76+
77+
var result = Kefir.combine({ a: aStream, b: bStream });
78+
result.log();
79+
80+
pre(title='console output')
81+
:escapehtml
82+
> [combine] <value> { a: 1, b: 2 }
83+
> [combine] <value> { a: 3, b: 2 }
84+
> [combine] <value> { a: 3, b: 4 }
85+
> [combine] <end>
86+
87+
pre(title='events in time').
88+
a: ----1----3X
89+
b: ------2----4X
90+
91+
result: ------•--•-•X
92+
93+
p.
94+
#[img(data-emoji="point_up")] If there are duplicate keys in both #[b obss]
95+
#[i and] #[b passiveObss], only the latest values from #[b obss] will appear
96+
in the combined object for the duplicated keys.
97+
6498
p.
6599
The result stream emits a value only when it has at least one value from each of source observables.
66100
Ends when all the active source observables (#[b obss] array) end.
@@ -70,7 +104,6 @@ p.
70104

71105

72106

73-
74107
+descr-method('zip', 'zip', 'Kefir.zip(sources, [combinator])', 'obs.zip(otherObs, [combinator])').
75108
Creates a stream with values from #[b sources]
76109
lined up with each other. For example if you have two sources with values

kefir.js.flow

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ declare var Kefir: {
151151

152152
combine<E>(obss: Observable<any,E>[], combinator?: Function): Observable<any,E>;
153153
combine<E>(obss: Observable<any,E>[], passiveObss?: Observable<any,E>[], combinator?: Function): Observable<any,E>;
154+
combine<E>(obss: {[key:string]:Observable<any,E>}, combinator?: Function): Observable<any,E>;
155+
combine<E>(obss: {[key:string]:Observable<any,E>}, passiveObss?: {[key:string]:Observable<any,E>}, combinator?: Function): Observable<any,E>;
156+
154157
zip<V,E>(obss: Observable<V,E>[]): Observable<Array<V>,E>;
155158
zip<E>(obss: Observable<any,E>[], combinator: Function): Observable<any,E>;
156159
merge<V,E>(obss: Observable<V,E>[]): Observable<V,E>;

src/many-sources/combine.js

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import {concat, fillArray} from '../utils/collections';
55
import {spread} from '../utils/functions';
66
import never from '../primary/never';
77

8-
8+
function collect(source, keys, values) {
9+
for (var prop in source) {
10+
if( source.hasOwnProperty( prop ) ) {
11+
keys.push(prop);
12+
values.push(source[prop]);
13+
}
14+
}
15+
}
916

1017
function defaultErrorsCombinator(errors) {
1118
let latestError;
@@ -23,7 +30,7 @@ function Combine(active, passive, combinator) {
2330
Stream.call(this);
2431
this._activeCount = active.length;
2532
this._sources = concat(active, passive);
26-
this._combinator = combinator ? spread(combinator, this._sources.length) : (x => x);
33+
this._combinator = combinator;
2734
this._aliveCount = 0;
2835
this._latestValues = new Array(this._sources.length);
2936
this._latestErrors = new Array(this._sources.length);
@@ -153,11 +160,43 @@ inherit(Combine, Stream, {
153160

154161
});
155162

163+
function combineAsArray(active, passive = [], combinator) {
164+
if (!Array.isArray(passive)) {
165+
throw new Error('Combine can only combine active and passive collections of the same type.');
166+
}
167+
168+
combinator = combinator ? spread(combinator, active.length + passive.length) : (x => x);
169+
return active.length === 0 ? never() : new Combine(active, passive, combinator);
170+
}
171+
172+
function combineAsObject(active, passive = {}, combinator) {
173+
if (typeof passive !== 'object' || Array.isArray(passive)) {
174+
throw new Error('Combine can only combine active and passive collections of the same type.');
175+
}
156176

157-
export default function combine(active, passive = [], combinator) {
177+
let keys = [],
178+
activeObservables = [],
179+
passiveObservables = [];
180+
181+
collect(active, keys, activeObservables);
182+
collect(passive, keys, passiveObservables);
183+
184+
const objectify = values => {
185+
let event = {};
186+
for(let i = values.length - 1; 0 <= i; i--) {
187+
event[keys[i]] = values[i];
188+
}
189+
return combinator ? combinator(event) : event;
190+
}
191+
192+
return activeObservables.length === 0 ? never() : new Combine(activeObservables, passiveObservables, objectify);
193+
}
194+
195+
export default function combine(active, passive, combinator) {
158196
if (typeof passive === 'function') {
159197
combinator = passive;
160-
passive = [];
198+
passive = undefined;
161199
}
162-
return active.length === 0 ? never() : new Combine(active, passive, combinator);
200+
201+
return Array.isArray(active) ? combineAsArray(active, passive, combinator) : combineAsObject(active, passive, combinator);
163202
}

0 commit comments

Comments
 (0)